How to change a module variable from another modul

2019-01-06 09:42发布

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?

3条回答
别忘想泡老子
2楼-- · 2019-01-06 09:54

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)

查看更多
不美不萌又怎样
3楼-- · 2019-01-06 10:14

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  # … with 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.

查看更多
爷、活的狠高调
4楼-- · 2019-01-06 10:17

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). So when you assign a new value to a, you just change 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 are actually 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.

查看更多
登录 后发表回答