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