How to change a module variable from another module?
PythonImportModulePython Problem Overview
Suppose I have a package named bar
, and it contains bar.py
:
a = None
def foobar():
print a
and __init__.py
:
from bar import a, foobar
Then I execute this script:
import bar
print bar.a
bar.a = 1
print bar.a
bar.foobar()
Here's what I expect:
None
1
1
Here's what I get:
None
1
None
Can anyone explain my misconception?
Python Solutions
Solution 1 - Python
You are using from bar import a
. a
becomes a symbol in the global scope of the importing module (or whatever scope the import statement occurs in).
When you assign a new value to a
, you are just changing which value a
points too, not the actual value. Try to import bar.py
directly with import bar
in __init__.py
and conduct your experiment there by setting bar.a = 1
. This way, you will actually be modifying bar.__dict__['a']
which is the 'real' value of a
in this context.
It's a little convoluted with three layers but bar.a = 1
changes the value of a
in the module called bar
that is actually derived from __init__.py
. It does not change the value of a
that foobar
sees because foobar
lives in the actual file bar.py
. You could set bar.bar.a
if you wanted to change that.
This is one of the dangers of using the from foo import bar
form of the import
statement: it splits bar
into two symbols, one visible globally from within foo
which starts off pointing to the original value and a different symbol visible in the scope where the import
statement is executed. Changing a where a symbol points doesn't change the value that it pointed too.
This sort of stuff is a killer when trying to reload
a module from the interactive interpreter.
Solution 2 - Python
One source of difficulty with this question is that you have a program named bar/bar.py
: import bar
imports either bar/__init__.py
or bar/bar.py
, depending on where it is done, which makes it a little cumbersome to track which a
is bar.a
.
Here is how it works:
The key to understanding what happens is to realize that in your __init__.py
,
from bar import a
in effect does something like
a = bar.a
# … where bar = bar/bar.py (as if bar were imported locally from __init__.py)
and defines a new variable (bar/__init__.py:a
, if you wish). Thus, your from bar import a
in __init__.py
binds name bar/__init__.py:a
to the original bar.py:a
object (None
). This is why you can do from bar import a as a2
in __init__.py
: in this case, it is clear that you have both bar/bar.py:a
and a distinct variable name bar/__init__.py:a2
(in your case, the names of the two variables just happen to both be a
, but they still live in different namespaces: in __init__.py
, they are bar.a
and a
).
Now, when you do
import bar
print bar.a
you are accessing variable bar/__init__.py:a
(since import bar
imports your bar/__init__.py
). This is the variable you modify (to 1). You are not touching the contents of variable bar/bar.py:a
. So when you subsequently do
bar.foobar()
you call bar/bar.py:foobar()
, which accesses variable a
from bar/bar.py
, which is still None
(when foobar()
is defined, it binds variable names once and for all, so the a
in bar.py
is bar.py:a
, not any other a
variable defined in another module—as there might be many a
variables in all the imported modules). Hence the last None
output.
Conclusion: it is best to avoid any ambiguity in import bar
, by not having any bar/bar.py
module (since bar.__init__.py
makes directory bar/
a package already, that you can also import with import bar
).
Solution 3 - Python
To put another way: Turns out this misconception is very easy to make. It is sneakily defined in the Python language reference: the use of object instead of symbol. I would suggest that the Python language reference make this more clear and less sparse..
> The from
form does not bind the module name: it goes through the
> list of identifiers, looks each one of them up in the module found in
> step (1), and binds the name in the local namespace to the object thus
> found.
HOWEVER:
When you import, you import the current value of the imported symbol and add it to your namespace as defined. You are not importing a reference, you are effectively importing a value.
Thus, to get the updated value of i
, you must import a variable that holds a reference to that symbol.
In other words, importing is NOT like an import
in JAVA, external
declaration in C/C++ or even a use
clause in PERL.
Rather, the following statement in Python:
from some_other_module import a as x
is more like the following code in K&R C:
extern int a; /* import from the EXTERN file */
int x = a;
(caveat: in the Python case, "a" and "x" are essentially a reference to the actual value: you're not copying the INT, you're copying the reference address)