TL;博士:
在 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__
设置为创建它们的目录。
当你这样做import foo.bar
时,上面的搜索首先发生 for 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__)
尚未更新以包含隐式命名空间包。