为什么 from ... import ... 语句包含隐式导入?

fel*_*nho 5 python python-import python-3.x python-packaging

给定一个包:

package/
??? __init__.py
??? module.py
Run Code Online (Sandbox Code Playgroud)

__init__.py:

from .module import function
Run Code Online (Sandbox Code Playgroud)

模块.py:

def function():
    pass
Run Code Online (Sandbox Code Playgroud)

可以导入包并打印其命名空间。

python -c 'import package; print(dir(package))'
Run Code Online (Sandbox Code Playgroud)
['__builtins__', ..., 'function', 'module']

Run Code Online (Sandbox Code Playgroud)

题:

为什么仅在导入时package包含的命名空间?modulefunction__init__.py

我原以为package的命名空间将只包含function而不是module. 文档中也提到了这种机制,

“当使用任何机制(例如 importlib API、import 或 import-from 语句或内置__import__())加载子模块时,将在父模块的命名空间中放置到子模块对象的绑定。”

但并没有真正的动机。对我来说,这个选择似乎很奇怪,因为我认为子模块是结构包的实现细节,并且不希望它们成为 API 的一部分,因为结构可以改变。

我也知道“Python 是为了成年人的同意”,并且不能真正向用户隐藏任何东西。但我会争辩说,将子模块名称绑定到包的范围会使用户不太清楚什么是 API 的实际组成部分以及什么可以更改。

为什么不使用__sub_modules__属性等来使用户可以访问子模块?这个设计决定的原因是什么?

use*_*ica 3

您说您将子模块视为实现细节。这不是子模块背后的设计意图;它们可以并且非常常见地是包的公共接口的一部分。导入系统旨在促进对子模块的访问,而不是阻止访问。

加载子模块会将绑定放置到父级的命名空间中,因为这是访问该模块所必需的。例如,在以下代码之后:

import package.submodule
Run Code Online (Sandbox Code Playgroud)

表达式的package.submodule计算结果必须为子模块的模块对象。package计算结果为包的模块对象,因此该模块对象必须具有submodule引用子模块的模块对象的属性。

此时,您几乎肯定会想:“嘿,没有理由from .submodule import function必须做同样的事情!” 它执行相同的操作,因为此属性绑定是子模块初始化的一部分,仅在第一次导入时发生,并且无论触发哪种导入都需要执行相同的设置。

这并不是一个非常有力的理由。通过足够的更改和重新调整,导入系统绝对可以按照您期望的方式设计。它不是这样设计的,因为设计师和你有不同的优先事项。Python 的设计很少关心隐藏事物或支持任何隐私概念。