我正在玩示例以回答question posted here on SO并且发现很难理解python的import *
弄乱范围的机制。
首先是一些背景:这个问题不涉及实际问题;我很清楚from foo import *
是不赞成的(这是正确的)并且我认为它是的原因比代码中的清晰度更深。我的兴趣在于通过循环import *
s 来理解导致不良行为的机制。换句话说,我理解 预期观察到的行为;我不明白为什么。
我无法理解的情况是使用{从导入的模块(b
)引用导入模块(a
)时出现的问题{1}}。当导入模块使用*
时,我设法观察到行为的细微差别,但整体(坏)行为是相同的。我既没有在文件中也没有在SO上找到任何明确的解释。
通过范围内可用的内容调查行为,我设法构建了一个小例子,根据上面提到的问题以及我在SO和其他地方进行的一些搜索来说明其内容的差异。我试着尽可能简明扼要地展示。下面的所有代码和实验都是使用python 2.7.8完成的。
工作方案
首先是一个包含一个包含一个类*
:
a.py
客户端代码的第一个变体,导入模块a,class A:
pass
:
b_v1.py
相同代码的第二个变体,从模块from pprint import pprint
def dump_frame(offset=0):
import sys
frame = sys._getframe(1+offset)
d = frame.f_globals
d.update(frame.f_locals)
return d
print 'before import v1'
pprint (dump_frame())
import a
print 'after import v1'
pprint (dump_frame())
print a.A()
导入*
,a
:
b_v2.py
from pprint import pprint
def dump_frame(offset=0):
import sys
frame = sys._getframe(1+offset)
d = frame.f_globals
d.update(frame.f_locals)
return d
print 'before import v2'
pprint (dump_frame())
from a import *
print 'after import v2'
pprint (dump_frame())
print A()
和b_v1
会产生相同的输出,并且两者都能够按预期实例化A。然而,在导入之后,正如预期的那样,它们会有所不同。我强调了区别: b_v2
,范围
b_v1.py
而'a': <module 'a' from '.../stackoverflow/instance/a.py'>
没有,但有
b_v2.py
导入前后,范围包含'A': <class a.A at 0x...>
设置为__builtins__
。
两种变体都成功实例化<module '__builtin__' (built-in)>
。
不工作的情况
有趣的行为是将A
更改为包含a.py
的循环引用(在b
和b_v1
变体中)。
调整后的b_v2
代码:
a.py
(为了简洁起见,只显示了from b_v1 import *
class A:
pass
的一个案例;显然在a.py
的情况下导入的是此模块,而不是b_v2.py
)
在我对带有循环引用的场景中范围内容的观察中,我看到:
在两种变体中,b_v1.py
中导入之前,a
与上述情况类似。但是,导入后,它会被更改并包含__builtins__
&#39;算术错误&#39;: &#39; AssertionError&#39;:, &#39; AttributeError&#39; :, ...
这是不必要的长时间粘贴在这里。
dict
出现两次。我可以理解这是导入的结果,如果代码在函数内部,可能不会发生。在变体__builtins__
中,模块b_v2
出现在范围内;它存在于变体a
中。
在两种变体中,b_v1
的实例化都失败了。鉴于变量A
中的模块存在于范围内(因此,我假设已成功导入),我原本希望能够实例化b_v1
。不是这种情况。但是有一些区别:如果A
,则b_v1.py
失败,AttributeError: 'module' object has no attribute 'A'
失败,失败为b_v2.py
。在后一种情况下,它始终是相同的错误,与我是否尝试实例化为NameError
的{{1}}(如工作示例中)无关。
总结我的问题:
通过什么机制,广告A()
会弄乱范围?
为什么在b_v1的情况下无法实例化a.A()
,尽管模块在范围内?
答案 0 :(得分:4)
Python模块从上到下执行。 Import语句可以像任何其他语句一样执行。当运行import语句时,它会执行这些操作(出于说明目的简化,有关完整详细信息,请参阅language reference):
sys.modules
中。如果是,立即归还sys.modules
中为模块创建一个空条目,并带有一个空命名空间。假设我们有这样的文件:
a.py
:
from b import *
foo = object()
b.py
:
from a import *
print(repr(foo))
进一步假设首先导入a.py
。让我们逐行完成:
a
。在我们开始执行a
之前,sys.modules['a']
的引用存储在a.py
中。from b import *
运行import b
。这转换为“b
,然后从a
的命名空间中取出所有内容到sys.modules['b']
的命名空间。”b.py
from a import *
运行a
。 Python导入a
。sys.modules['a']
存在以来,a.py
的导入会立即返回。foo = object()
尚未执行a.foo
,b
尚不存在,因此无法将其转储到b.py
的命名空间中。NameError
在{{1}}上崩溃。