__init__.py 中的本地范围与相对导入

Bła*_*lik 9 python python-importlib

我注意到asyncio/init.py从 python 3.6 开始使用以下构造:

from .base_events import *

...

__all__ = (base_events.__all__ + ...)
Run Code Online (Sandbox Code Playgroud)

base_events符号未在源代码中的任何位置导入,但模块仍包含它的局部变量。

我已经使用以下代码检查了这种行为,并将其放入一个旁边__init__.py的虚拟对象中test.py

test = "not a module"
print(test)

from .test import *
print(test)
Run Code Online (Sandbox Code Playgroud)

不是模块
<module 'testpy.test' from 'C:\Users\MrM\Desktop\testpy\test.py'>

这意味着test在使用星型导入后变量被隐藏了。

我稍微摆弄了一下,结果发现它不一定是一个星型 import,但它必须在一个 内__init__.py,而且它必须是相对的。否则模块对象不会被分配到任何地方。

如果没有赋值,从一个不是 的文件运行上面的例子__init__.py会引发一个NameError.

这种行为从何而来?这是否已在某处的导入系统规范中进行了概述?__init__.py必须以这种方式与众不同的原因是什么?它不在参考资料中,或者至少我找不到。

tde*_*ney 8

此行为在导入系统文档部分5.4.2 子模块中定义

当使用任何机制(例如 importlib API、import 或 import-from 语句或内置import ())加载子模块时,将在父模块的命名空间中放置到子模块对象的绑定。例如,如果包 spam 有一个子模块 foo,那么在导入 spam.foo 后,spam 将有一个属性 foo 绑定到子模块。

包命名空间包括在中创建的命名空间__init__.py以及导入系统添加的额外内容。的原因是命名空间的一致性。

鉴于 Python 熟悉的名称绑定规则,这似乎令人惊讶,但它实际上是导入系统的一个基本特性。不变的是,如果你有 sys.modules['spam'] 和 sys.modules['spam.foo'] (就像你在上面导入之后那样),后者必须作为前者的 foo 属性出现。