据我从了解Python文档,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)
?
重要的是,模块最多只能加载一次(除非明确地重新加载了模块)。如果将一个模块导入多个模块,则所有模块都引用同一模块对象。例如:
模块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
执行该语句时,它基本上等效于execute from root.another_package import bar
。由于another_package
确实是root
包中的模块,所以该语句成功执行,并产生以下效果(可能还有更多效果,但是为此,我们将重点放在这3个方面):
root
加载如果以前装(它的__init__.py
运行)bar
导入到当前名称空间another_package
作为属性添加到root
模块对象一些开发人员并不完全了解第1项和第3项。
回到您的问题:让我们import root.package
按顺序查看执行时会发生什么:
root
的__init__.py
已运行(因为root
尚未加载)package
的__init__.py
已运行(因为package
尚未加载)from ..another_package import bar
执行具有上述副作用的程序,最明显的是(是,对象。每个模块只有一个,还记得吗?)添加root
了属性的模块对象another_package
。这就解释了为什么another_package
在出现root
的dir
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句