TL;DR:
在 Python 3.3 上,您无需执行任何操作,只需不要将任何 __init__.py 放在您的命名空间包目录中,它就会正常工作。在 pre-3.3 上,选择 pkgutil.extend_path() 解决方案而不是 pkg_resources.declare_namespace() 解决方案,因为它是面向未来的并且已经与隐式命名空间包兼容。
Python 3.3 引入了隐式命名空间包,请参阅PEP 420。
这意味着现在import foo 可以创建三种类型的对象:
-
foo.py 文件表示的模块
- 一个常规包,由一个目录
foo 表示,其中包含一个__init__.py 文件
- 一个命名空间包,由一个或多个目录
foo 表示,没有任何__init__.py 文件
包也是模块,但这里我说的“模块”是指“非包模块”。
首先它扫描sys.path 以查找模块或常规包。如果成功,它将停止搜索并创建和初始化模块或包。如果它没有找到模块或常规包,但它至少找到一个目录,它会创建并初始化一个命名空间包。
模块和常规包将__file__ 设置为创建它们的.py 文件。常规和命名空间包将 __path__set 设置为创建它们的目录。
当您执行import foo.bar 时,上述搜索首先针对foo,然后如果找到一个包,则以foo.__path__ 作为搜索路径而不是sys.path 来搜索bar。如果找到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 和@ 987654357@ 都可以。
pkg_resources.declare_namespace(__name__) 尚未更新为包含隐式命名空间包。