据我所知python docs,from package import x
语句应仅绑定x
,而不绑定package
到当前命名空间。
但实际上,如果package
是相对名称, 的名称有时会被绑定!
让我举个例子。请考虑以下文件层次结构:
root/
package/
__init__.py
subpackage/
__init__.py
子包/ __ INIT __ PY:
foo = 42
包/ __ INIT __ PY:
from os import name
from .subpackage import foo
print(globals().get('name'))
print(globals().get('os'))
print(globals().get('foo'))
print(globals().get('subpackage'))
现在让我们从root
目录运行python(v2或v3)解释器并执行
>>> import package
前三个输出行是可预测的:
posix
None
42
但最后一个是<module 'package.subpackage' ...>
而不是None
,这让我感到困惑。
我错过了什么吗?这是预期的行为吗?是什么原因?
在这种情况下,情况似乎更加奇怪:
root/
__init__.py # Empty.
package/
__init__.py
another_package/
__init__.py
another_package / __ INIT __ PY:
bar = 33
包/ __ INIT __ PY:
from ..another_package import bar
print(globals().get('another_package'))
现在我在 之外运行
>>> import root.package
None # OK.
>>> dir(root.package)
['__builtins__', ..., '__path__', 'bar'] # OK.
>>> dir(root)
['__builtins__', ..., '__path__', 'another_package', 'package'] # What?!
为什么another_package
出现在dir(root)
?
答案 0 :(得分:1)
实现模块最多加载一次非常重要(除非它们明确地reloaded)。如果在多个模块中导入模块,则所有模块都引用相同的模块 object 。 E.g:
模块M.py
bar = 10
模块A.py
import M
M.bar = 4
模块B.py
import M
M.bar = 6
所以:
>>> import M
>>> M.bar
10
>>> import A
>>> M.bar # A is referencing the same M module object!!
4
>>> import B
>>> M.bar # B is referencing the same M module object!!
6
现在,当执行语句from ..another_package import bar
时,它基本上等同于执行from root.another_package import bar
。由于another_package
确实是root
包中的一个模块,因此该语句会成功,并产生以下效果(可能会有更多,但出于此目的,请关注这些3):< / p>
root
已装入,如果之前未加载(已运行&#39; __init__.py
)bar
已导入当前命名空间another_package
作为属性添加到root
模块对象 有些开发人员并不完全了解第1项和第3项。
回到你的问题:让我们看看执行import root.package
时会发生什么,按此顺序:
root
&#39; __init__.py
已投放(因为root
尚未加载)package
&#39; __init__.py
已投放(因为package
尚未加载)from ..another_package import bar
被执行,它具有上面提到的副作用,最值得注意的是(是的。对象。每个模块只有一个,记得吗?)root
的模块对象具有属性another_package
已添加到其中。这解释了another_package
root
中出现dir
的原因。