Python 如何从另一个模块更改模块变量?
假设我有一个名为Python 如何从另一个模块更改模块变量?,python,import,module,Python,Import,Module,假设我有一个名为bar的包,它包含bar.py: a = None def foobar(): print a from bar import a, foobar 和\uuuu init\uuuuu.py: a = None def foobar(): print a from bar import a, foobar 然后我执行这个脚本: import bar print bar.a bar.a = 1 print bar.a bar.foobar() 以下是我
bar
的包,它包含bar.py
:
a = None
def foobar():
print a
from bar import a, foobar
和\uuuu init\uuuuu.py
:
a = None
def foobar():
print a
from bar import a, foobar
然后我执行这个脚本:
import bar
print bar.a
bar.a = 1
print bar.a
bar.foobar()
以下是我的期望:
None
1
1
以下是我得到的:
None
1
None
有人能解释我的误解吗?您使用的是bar import a的
a
成为导入模块的全局范围(或导入语句所在的任何范围)中的符号
当您为a
指定一个新值时,您只是在更改a
所指的值,而不是实际值。尝试使用中的导入bar
直接导入bar.py
,并通过设置bar.a=1
在那里进行实验。这样,您实际上将修改条。
这是a
在此上下文中的“真实”值
它有点复杂,有三层,但是bar.a=1
改变了名为bar
的模块中a
的值,该模块实际上是从\uu init\uuuuuuuuupy
派生的。它不会更改foobar
看到的a
的值,因为foobar
存在于实际文件bar.py
中。如果您想更改,可以设置bar.bar.a
这是使用import
语句的from foo import bar
形式的危险之一:它将bar
拆分为两个符号,一个从foo
中全局可见的符号,它开始指向原始值,并在执行import
语句的范围内显示另一个符号。更改符号指向的位置也不会更改其指向的值
当试图从交互式解释器中重新加载模块时,这类东西是一个杀手。这个问题的一个困难来源是您有一个名为bar/bar.py
:import bar
导入bar/\u init\uuuuuuuuupy
或bar/bar.py
,具体取决于导入的位置,这使得跟踪哪个a
是bar.a
有点麻烦
以下是它的工作原理:
理解发生了什么的关键是要认识到在\uuuu init\uuuuuuuuy.py
中
from bar import a
实际上是这样的
a = bar.a
# … where bar = bar/bar.py (as if bar were imported locally from __init__.py)
并定义一个新变量(bar/\uuuu init\uuuu.py:a
,如果您愿意)。因此,您的从bar导入\uuuu init\uuuuu.py
中的将名称bar/\uuuuuuu init\uuuuuuuu.py:a
绑定到原始bar.py:a
对象(无
)。这就是为什么您可以在\uuuu init\uuuuuuuuuupy
中从bar导入a作为a2
的原因:在这种情况下,很明显,您既有bar/bar.py:a
又有一个不同的变量名bar/\uuuuuuuu init\uuuuuuuupy:a2
(在您的例子中,这两个变量的名称恰好都是a
,但它们仍然存在于不同的名称空间中:在中,它们是bar.a
和a
)
现在,当你
import bar
print bar.a
您正在访问变量bar/\uu init\uuuuuuuuuuupy.a
(因为import bar
导入您的bar/\uuuuuuuuuuuuuuuuuuupy.py
),这是您修改的变量(修改为1)。您没有接触变量bar/bar.py:a
的内容。因此,当您随后执行此操作时
bar.foobar()
调用bar/bar.py:foobar()
,它从bar/bar.py
访问变量a
,该变量仍然是None
(当foobar()时)
已定义,它一次性绑定变量名,因此bar.py
中的a
是bar.py:a
,而不是在另一个模块中定义的任何其他a
变量,因为在所有导入的模块中可能有许多a
变量)。因此最后的无
输出
结论:最好不要使用任何bar/bar.py
模块(因为bar.\uu init\uuuu.py
使目录bar/
成为一个包,您也可以使用import bar
导入),从而避免import bar
中的任何歧义。换句话说:
事实证明,这种误解很容易产生。
使用对象而不是符号。我建议Python语言参考使这一点更清晰,更少稀疏
来自
的表单不绑定模块名称:它通过
标识符列表,在中找到的模块中查找每个标识符
步骤(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,而是在复制引用地址)事实上,我发现Python的导入方式比Java的要干净得多,因为名称空间/作用域应始终保持适当的分离,并且不会以意外的方式相互干扰。例如:更改名称空间中对象与名称的绑定(读:将某个内容分配给模块的全局属性)绝不会影响Python中的其他名称空间(读取:导入的引用),但会影响Java等。在Python中,您只需要了解导入的内容,而在Java中,您还必须了解其他模块,以防它改变