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

假设我有一个名为 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

有人能解释一下我的误解吗?

74725 次浏览

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

当你给 a赋一个新值时,你只是改变了 a的值,而不是实际值。尝试直接导入 bar.py__init__.py中的 import bar,并通过设置 bar.a = 1在那里进行实验。通过这种方式,您实际上将修改 bar.__dict__['a'],它是此上下文中 a的“实际”值。

这是一个有点复杂的三层,但 bar.a = 1改变了 a的价值在模块称为 bar,实际上是从 __init__.py派生。它不改变 foobar看到的 a的值,因为 foobar位于实际文件 bar.py中。如果你想改变它,你可以设置 bar.bar.a

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

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

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

它是这样运作的:

理解发生了什么的关键是要意识到在你的 __init__.py中,

from bar import a

实际上做了类似于

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

并定义一个新变量(如果您愿意,可以定义为 bar/__init__.py:a)。因此,__init__.py中的 from bar import a将名称 bar/__init__.py:a绑定到原始 bar.py:a对象(None)。这就是为什么你可以在 __init__.py中使用 from bar import a as a2: 在这个例子中,很明显你同时拥有 bar/bar.py:afrom bar import a4变量名 bar/__init__.py:a2(在你的例子中,这两个变量的名字碰巧都是 from bar import a0,但是它们仍然存在于不同的名称空间中: 在 __init__.py中,它们是 from bar import a2和 from bar import a0)。

等你找到了

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(),它从 bar/bar.py访问变量 abar/bar.py仍然是 None(定义 foobar()时,它一次性绑定变量名,所以 bar.py中的 abar.py:a,而不是另一个模块中定义的任何其他 a变量ーー因为在所有导入的模块中可能有许多 a变量)。因此,最后的 None输出。

结论: 最好避免 import bar中的任何歧义,通过 没有拥有任何 bar/bar.py模块(因为 bar.__init__.py已经使目录 bar/成为一个包,您也可以用 import bar导入)。

换句话说: 事实证明,这种误解很容易产生。 它是在 Python 语言引用中秘密定义的: 使用 对象而不是 符号。我建议,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,而是在复制引用地址)