在 Python 中,命名空间包允许您在多个项目之间传播 Python 代码。当您想要将相关库作为单独的下载发布时,这很有用。例如,使用 PYTHONPATH
中的目录 Package-1
和 Package-2
,
Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py
最终用户可以import namespace.module1
和import namespace.module2
。
定义一个命名空间包的最佳方式是什么,以便多个 Python 产品可以在该命名空间中定义模块?
最佳答案
TL;DR:
在 Python 3.3 上,您无需执行任何操作,只需不要将任何 __init__.py
放入您的命名空间包目录中,它就会正常工作。在 3.3 之前,选择 pkgutil.extend_path()
解决方案而不是 pkg_resources.declare_namespace()
解决方案,因为它是面向 future 的并且已经与隐式命名空间包兼容。
Python 3.3 引入了隐式命名空间包,见 PEP 420 .
这意味着现在可以通过 import foo
创建三种类型的对象:
foo.py
文件表示的模块__init__.py
文件的目录 foo
表示foo
表示,没有任何 __init__.py
文件包也是模块,但这里我说的“模块”是指“非包模块”。
首先它会扫描 sys.path
以查找模块或常规包。如果成功,它将停止搜索并创建和初始化模块或包。如果它没有找到模块或常规包,但它至少找到一个目录,它会创建并初始化一个命名空间包。
模块和常规包将 __file__
设置为创建它们的 .py
文件。常规和命名空间包的 __path__
设置为创建它们的目录。
当你执行 import foo.bar
时,上面的搜索首先发生在 foo
,然后如果找到一个包,则搜索 bar
使用 foo.__path__
作为搜索路径而不是 sys.path
。如果找到 foo.bar
,则创建并初始化 foo
和 foo.bar
。
那么常规包和命名空间包是如何混合的呢?通常它们不会,但旧的 pkgutil
显式命名空间包方法已扩展为包含隐式命名空间包。
如果您有一个现有的常规包,其中包含这样的 __init__.py
:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
...遗留行为是将搜索到的路径上的任何其他常规包添加到其__path__
。但在 Python 3.3 中,它还添加了命名空间包。
所以你可以有如下的目录结构:
├── path1
│ └── package
│ ├── __init__.py
│ └── foo.py
├── path2
│ └── package
│ └── bar.py
└── path3
└── package
├── __init__.py
└── baz.py
...只要两个 __init__.py
有 extend_path
行(和 path1
、path2
和 path3
在你的 sys.path
) import package.foo
, import package.bar
和 import package.baz
一切正常。
pkg_resources.declare_namespace(__name__)
尚未更新为包含隐式命名空间包。
https://stackoverflow.com/questions/1675734/