如何从另一个模块更改模块变量?

问题描述 投票:81回答:3

假设我有一个名为bar的包,它包含bar.py

a = None

def foobar():
    print a

__init__.py

from bar import a, foobar

然后我执行这个脚本:

import bar

print bar.a
bar.a = 1
print bar.a
bar.foobar()

这就是我的期望:

None
1
1

这是我得到的:

None
1
None

任何人都能解释我的错误观念吗?

python import module
3个回答
74
投票

您正在使用from bar import aa成为导入模块的全局范围中的符号(或导入语句出现的任何范围)。

当您为a分配新值时,您只是更改a指向的值,而不是实际值。尝试直接用bar.py中的import bar导入__init__.py并通过设置bar.a = 1在那里进行实验。这样,您实际上将修改bar.__dict__['a'],这是a在此上下文中的“真实”值。

这有点令人费解,有三层,但bar.a = 1a模块中更改了bar的值,该模块实际上是从__init__.py派生出来的。它没有改变a看到的foobar的值,因为foobar生活在实际文件bar.py中。如果你想改变它,你可以设置bar.bar.a

这是使用from foo import bar形式的import形式的危险之一:它将bar分成两个符号,一个从foo内部全局可见,它开始指向原始值,并且在import语句的范围内可见的不同符号执行。更改符号指向的位置不会更改它指向的值。

当从交互式解释器尝试reload模块时,这种东西是一个杀手。


18
投票

这个问题的一个难点是你有一个名为bar/bar.py的程序:import bar进口bar/__init__.pybar/bar.py,取决于它在哪里完成,这使得跟踪哪个abar.a有点麻烦。

下面是它的工作原理:

理解发生的事情的关键是要在你的__init__.py中意识到

from bar import a

实际上做了类似的事情

a = bar.a  # … with bar = bar/bar.py (as if bar were imported locally from __init__.py)

并定义一个新变量(bar/__init__.py:a,如果你愿意)。因此,你在from bar import a__init__.py将名称bar/__init__.py:a绑定到原始的bar.py:a对象(None)。这就是为什么你可以在from bar import a as a2__init__.py:在这种情况下,很明显你有bar/bar.py:a和一个独特的变量名称bar/__init__.py:a2(在你的情况下,这两个变量的名称恰好都是a,但他们仍然活着在不同的名称空间中:在__init__.py中,它们是bar.aa)。

现在,当你这样做

import bar

print bar.a

你正在访问变量bar/__init__.py:a(因为import bar导入你的bar/__init__.py)。这是您修改的变量(为1)。你没有触及变量bar/bar.py:a的内容。所以当你随后做的时候

bar.foobar()

你打电话给bar/bar.py:foobar(),它从a访问变量bar/bar.py,它仍然是None(当定义foobar()时,它一劳永逸地绑定变量名称,所以a中的bar.pybar.py:a,而不是在另一个模块中定义的任何其他a变量 - 因为那里可能是所有导入模块中的许多a变量)。因此最后的None输出。


10
投票

换句话说:结果证明这种误解很容易。 It is sneakily defined in the Python language reference:使用对象而不是符号。我建议Python语言参考使这更清晰,更少稀疏..

from表单不绑定模块名称:它遍历标识符列表,在步骤(1)中找到的模块中查找每个标识符,并将本地名称空间中的名称绑定到找到的对象。

然而:

导入时,导入导入符号的当前值,并将其添加到定义的命名空间中。您没有导入引用,您实际上是在导入值。

因此,要获取i的更新值,必须导入一个包含对该符号的引用的变量。

换句话说,导入不像JAVA中的import,C / C ++中的external声明,甚至PERL中的use子句。

相反,Python中的以下语句:

from some_other_module import a as x

更像是K&R C中的以下代码:

extern int a; /* import from the EXTERN file */

int x = a;

(警告:在Python的情况下,“a”和“x”本质上是对实际值的引用:你没有复制INT,你正在复制引用地址)

© www.soinside.com 2019 - 2024. All rights reserved.