Strange effect with import

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Dec 20 17:13:13 EST 2012


On Thu, 20 Dec 2012 20:39:19 +0000, Jens Thoms Toerring wrote:

> Hi,
> 
>    I hope that this isn't a stupid question, asked already a
> hundred times, but I haven't found anything definitive on the problem I
> got bitten by. I have two Python files like this:
>
> -------- S1.py ------
> import random
> import S2
> 
> class R( object ) :
>     r = random.random( )
> 
> if __name__ == "__main__" :
>     print R.r
>     S2.p( )
> 
> -------- S2.py ------
> import S1
> 
> def p( ) :
>     print S1.R.r
> 
> and my expectation was that the static variable 'r' of class R 

The terminology we prefer here is "class attribute", not "static 
variable". Attributes are always assigned in dynamic storage, whether 
they are per-instance or on the class.



> would be
> identical when accessed from S1.py and S2.py. Unfortunately, that isn't
> the case, the output is different (and R seems to get instantiated
> twice).

You don't instantiate R at all. You only ever refer to the class object, 
you never instantiate it to create an instance. What you are actually 
seeing is a side-effect of the way Python modules are imported:

- Python modules are instances that are instantiated at import 
  time, and then cached by module name;

- the module name is *usually* the file name (sans .py extension), 
  except when you are running it as a script, in which case it 
  gets set to the special value "__main__" instead.

So the end result is that you actually end up with THREE module objects, 
__main__, S2 and S1, even though there are only two module *files*. Both 
__main__ and S1 are instantiated from the same source code and contain 
the same objects: both have a class called R, with fully-qualified names 
__main__.R and S1.R, but they are separate objects.


[...]
> or, alternatively, if I put the defintion of class R into a third file
> which I then import from the other 2 files, things suddenly start to
> work as expected/ Can someone explain what's going one here? I found
> this a bit surprising.

You have a combination of two tricky situations:

* A circular import: module S1 imports S2, and S2 imports S1.

* A .py file, S1.py, being used as both an importable module 
  and a runnable script.

Circular imports are usually hard to get rid at the best of time. 
Combined with the second factor, they can lead to perplexing errors, as 
you have just found out.


> This is, of course, not my "real" code - it would be much more sensible
> to pass the number to the function in the second file as an argument -
> but is the smallest possinle program I could come up with that
> demonstrate the problem. 

And let me say sincerely, thank you for doing so! You would be amazed how 
many people do not make any effort to simplify their problem before 
asking for help.


> In my "real" code it's unfortunately not
> possible to pass that number to whatever is going to use it in the
>  other file, I have to simulate a kind of global variable
> shared between different files.

Well, I find that hard to believe. "Not convenient"? I could believe 
that. "Difficult"? Maybe. "Tricky"? I could even believe that. But "not 
possible"? No, I don't believe that it is impossible to pass variables 
around as method arguments.



-- 
Steven



More information about the Python-list mailing list