假设我有一个名为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
任何人都可以解释我的误解吗?
答案 0 :(得分:73)
您正在使用from bar import a
。 a
成为导入模块的全局范围中的符号(或导入语句出现的任何范围)。
为a
分配新值时,您只是更改了a
个值,而不是实际值。尝试直接使用bar.py
中的import bar
导入__init__.py
,然后通过设置bar.a = 1
在那里进行实验。这样,您实际上将修改bar.__dict__['a']
,这是a
在此上下文中的“真实”值。
这有三个层次,但bar.a = 1
更改了a
模块中bar
的值,该值实际上来自__init__.py
。它不会更改a
看到的foobar
的值,因为foobar
位于实际文件bar.py
中。如果您想更改它,可以设置bar.bar.a
。
这是使用from foo import bar
声明的import
形式的危险之一:它将bar
拆分为两个符号,一个从foo
内开始全局可见off指向原始值以及在执行import
语句的范围内可见的不同符号。更改符号指向的位置不会更改它指向的值。
当从交互式解释器尝试reload
模块时,这种东西是杀手锏。
答案 1 :(得分:18)
这个问题的一个难点是你有一个名为bar/bar.py
的程序:import bar
导入bar/__init__.py
或bar/bar.py
,具体取决于它的执行位置,跟踪哪个a
是bar.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
和不同的变量名称{{1} (在你的情况下,两个变量的名称恰好都是a
,但它们仍然存在于不同的名称空间中:在__init__.py
中,它们是bar.a
和{{1} })。
现在,当你做
时a
您正在访问变量import bar
print bar.a
(因为bar/__init__.py:a
导入了您的import bar
)。这是您修改的变量(为1)。您没有触及变量bar/__init__.py
的内容。所以当你随后做
bar/bar.py:a
你调用bar.foobar()
,它从bar/bar.py:foobar()
访问变量a
,它仍然是bar/bar.py
(当定义None
时,它会绑定变量名一次,总而言之,foobar()
中的a
为bar.py
,而不是另一个模块中定义的任何其他bar.py:a
变量 - 因为所有a
变量都可能存在导入的模块)。因此,最后a
输出。
答案 2 :(得分: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&amp; R C中更像 以下代码:
extern int a; /* import from the EXTERN file */
int x = a;
(警告:在Python的情况下,&#34; a&#34;和&#34; x&#34;基本上是对实际值的引用:你没有复制INT,你&#39; ;重新复制参考地址)