为什么 from ... import ... 语句包含隐式导入?
Why does the from ... import ... statement contain an implicit import?
给定一个包裹:
package/
├── __init__.py
└── module.py
__init__.py:
from .module import function
module.py:
def function():
pass
可以导入包并打印其命名空间。
python -c 'import package; print(dir(package))'
['__builtins__', ..., 'function', 'module']
问题:
为什么 package
的命名空间包含 module
而 __init__.py
中只导入了 function
?
我原以为 package
的命名空间只包含 function
而不是 module
。 Documentation,
中也提到了这个机制
"When a submodule is loaded using any mechanism (e.g. importlib APIs,
the import or import-from statements, or built-in __import__()
) a
binding is placed in the parent module’s namespace to the submodule
object."
但并不是真正有动力。对我来说,这个选择似乎很奇怪,因为我认为子模块是构建包的实现细节,并且不希望它们成为 API 的一部分,因为结构可以改变。
我也知道 "Python is for consenting adults" 并且无法真正向用户隐藏任何内容。但我认为,将子模块名称绑定到包的范围会使用户不太清楚 API 的实际部分以及可以更改的内容。
为什么不使用 __sub_modules__
属性来让用户可以访问子模块?这个设计决定的原因是什么?
你说你认为子模块是实现细节。这不是子模块背后的设计意图;它们可以是,而且最常见的是,包的 public 接口的一部分。导入系统旨在促进子模块的访问,而不是阻止访问。
加载子模块会将绑定置于父级的名称空间中,因为这是访问模块所必需的。比如在下面的代码之后:
import package.submodule
表达式 package.submodule
的计算结果必须为子模块的模块对象。 package
评估为包的模块对象,因此此模块对象必须具有引用子模块的模块对象的 submodule
属性。
在这一点上,你几乎肯定会想,"hey, there's no reason from .submodule import function
has to do the same thing!"它做同样的事情,因为这个属性绑定是子模块初始化的一部分,它只发生在第一次导入时,并且需要做同样的事情无论哪种导入都会触发设置。
这不是一个非常有力的理由。通过足够多的更改和调整,导入系统肯定可以 设计成您期望的方式。它不是那样设计的,因为设计师的优先级与你不同。 Python 的设计很少关心隐藏内容或支持任何隐私概念。
你必须明白 Python 是一种运行时语言。 def
、class
和 import
都是可执行语句,它们在执行时将分别创建一个 function
、class
或 module
对象并将它们绑定到当前命名空间中。
wrt/ modules(包也是模块 - 至少在运行时),第一次为给定进程(直接或间接)导入模块时,匹配的 .py(好吧,通常它是编译的 .pyc version) 被执行(顶层的所有语句按顺序执行),生成的命名空间将用于填充 module
实例。只有完成此操作后,才能访问模块中定义的任何名称(您无法访问尚不存在的内容,可以吗?)。然后将模块对象缓存在 sys.modules
中以供后续导入。在此过程中,当加载子模块时,它被视为其父模块的属性。
For me this choice seems odd, as I think of sub-modules as implementation detail to structure packages and do not expect them to be part of the API as the structure can change
实际上,Python 的设计者考虑了相反的事情:"package"(注意在运行时没有 'package' 类型)主要是为了方便组织集合相关模块的 - IOW,̀moduleis the real building block - and as a matter of fact, at runtime, when what you import is technically a "package", it still materializes as a
module` 对象。
现在 "do not expect them to be part of the API as the structure can change",这当然已经被考虑在内了。这实际上是一种非常常见的模式,从单个模块开始,然后随着代码库的增长将其变成一个包——当然不会影响客户端代码。这里的关键是正确使用您的包的初始化程序 - __init__.py
文件 - 这实际上是您的包的 module
实例的构建来源。这让包充当 "facade",屏蔽 "implementation details" 的哪个子模块有效地定义了哪个函数,class 或其他。
所以这里的解决方案很简单,在你的包 __init__.py
中,1/ 导入你想要制作的名称 public (这样客户端代码可以直接从你的包导入而不是必须通过子模块)和 2/ 使用应被视为 public 的名称定义 __all__
属性,以便清楚地记录接口。
FWIW,最后一个操作也应该对所有子模块完成,您也可以对真正 "implementation details" 的东西使用 _single_leading_underscore 命名约定。
None 这当然会阻止任何人直接从您的子模块导入甚至 "private" 名称,但是当出现问题时它们会自行解决("we are all consenting adults" 等)。
给定一个包裹:
package/
├── __init__.py
└── module.py
__init__.py:
from .module import function
module.py:
def function():
pass
可以导入包并打印其命名空间。
python -c 'import package; print(dir(package))'
['__builtins__', ..., 'function', 'module']
问题:
为什么 package
的命名空间包含 module
而 __init__.py
中只导入了 function
?
我原以为 package
的命名空间只包含 function
而不是 module
。 Documentation,
"When a submodule is loaded using any mechanism (e.g. importlib APIs, the import or import-from statements, or built-in
__import__()
) a binding is placed in the parent module’s namespace to the submodule object."
但并不是真正有动力。对我来说,这个选择似乎很奇怪,因为我认为子模块是构建包的实现细节,并且不希望它们成为 API 的一部分,因为结构可以改变。
我也知道 "Python is for consenting adults" 并且无法真正向用户隐藏任何内容。但我认为,将子模块名称绑定到包的范围会使用户不太清楚 API 的实际部分以及可以更改的内容。
为什么不使用 __sub_modules__
属性来让用户可以访问子模块?这个设计决定的原因是什么?
你说你认为子模块是实现细节。这不是子模块背后的设计意图;它们可以是,而且最常见的是,包的 public 接口的一部分。导入系统旨在促进子模块的访问,而不是阻止访问。
加载子模块会将绑定置于父级的名称空间中,因为这是访问模块所必需的。比如在下面的代码之后:
import package.submodule
表达式 package.submodule
的计算结果必须为子模块的模块对象。 package
评估为包的模块对象,因此此模块对象必须具有引用子模块的模块对象的 submodule
属性。
在这一点上,你几乎肯定会想,"hey, there's no reason from .submodule import function
has to do the same thing!"它做同样的事情,因为这个属性绑定是子模块初始化的一部分,它只发生在第一次导入时,并且需要做同样的事情无论哪种导入都会触发设置。
这不是一个非常有力的理由。通过足够多的更改和调整,导入系统肯定可以 设计成您期望的方式。它不是那样设计的,因为设计师的优先级与你不同。 Python 的设计很少关心隐藏内容或支持任何隐私概念。
你必须明白 Python 是一种运行时语言。 def
、class
和 import
都是可执行语句,它们在执行时将分别创建一个 function
、class
或 module
对象并将它们绑定到当前命名空间中。
wrt/ modules(包也是模块 - 至少在运行时),第一次为给定进程(直接或间接)导入模块时,匹配的 .py(好吧,通常它是编译的 .pyc version) 被执行(顶层的所有语句按顺序执行),生成的命名空间将用于填充 module
实例。只有完成此操作后,才能访问模块中定义的任何名称(您无法访问尚不存在的内容,可以吗?)。然后将模块对象缓存在 sys.modules
中以供后续导入。在此过程中,当加载子模块时,它被视为其父模块的属性。
For me this choice seems odd, as I think of sub-modules as implementation detail to structure packages and do not expect them to be part of the API as the structure can change
实际上,Python 的设计者考虑了相反的事情:"package"(注意在运行时没有 'package' 类型)主要是为了方便组织集合相关模块的 - IOW,̀moduleis the real building block - and as a matter of fact, at runtime, when what you import is technically a "package", it still materializes as a
module` 对象。
现在 "do not expect them to be part of the API as the structure can change",这当然已经被考虑在内了。这实际上是一种非常常见的模式,从单个模块开始,然后随着代码库的增长将其变成一个包——当然不会影响客户端代码。这里的关键是正确使用您的包的初始化程序 - __init__.py
文件 - 这实际上是您的包的 module
实例的构建来源。这让包充当 "facade",屏蔽 "implementation details" 的哪个子模块有效地定义了哪个函数,class 或其他。
所以这里的解决方案很简单,在你的包 __init__.py
中,1/ 导入你想要制作的名称 public (这样客户端代码可以直接从你的包导入而不是必须通过子模块)和 2/ 使用应被视为 public 的名称定义 __all__
属性,以便清楚地记录接口。
FWIW,最后一个操作也应该对所有子模块完成,您也可以对真正 "implementation details" 的东西使用 _single_leading_underscore 命名约定。
None 这当然会阻止任何人直接从您的子模块导入甚至 "private" 名称,但是当出现问题时它们会自行解决("we are all consenting adults" 等)。