在Python中,命名空间包允许您在多个项目中传播Python代码。当您想要将相关库作为单独的下载发布时,这非常有用。例如,Package-1
中的目录Package-2
和PYTHONPATH
,
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产品可以在该命名空间中定义模块?
答案 0 :(得分:77)
有一个名为pkgutil的标准模块,您可以使用它 可以'将'模块附加到给定的命名空间。
使用您提供的目录结构:
Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py
您应该将这两行放在Package-1/namespace/__init__.py
和Package-2/namespace/__init__.py
(*)中:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
(*因为 - 你没有说明他们之间的依赖关系 - 你不知道他们中的哪一个将被首先识别 - 请参阅PEP 420以获取更多信息)
正如documentation所说:
这将添加到软件包的
__path__
目录中以sys.path
命名的目录的所有子目录。
从现在开始,您应该能够独立分发这两个包。
答案 1 :(得分:57)
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
文件foo
文件__init__.py
表示
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
在{{1} {},} sys.path
,import package.foo
和import package.bar
都可以。
import package.baz
尚未更新为包含隐式命名空间包。
答案 2 :(得分:5)
This section should be pretty self-explanatory.
简而言之,将命名空间代码放在__init__.py
中,更新setup.py
以声明命名空间,然后您可以自由选择。
答案 3 :(得分:2)
这是一个老问题,但有人最近在我的博客上评论说我关于命名空间包的帖子仍然相关,所以我想在这里链接到它,因为它提供了一个如何实现它的实际例子:
链接到本文的主要内容是:
http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package
__import__("pkg_resources").declare_namespace(__name__)
技巧几乎推动了TiddlyWeb中插件的管理,到目前为止似乎已经有了。
答案 4 :(得分:-8)
您将Python命名空间概念放在前面,在python中无法将包放入模块中。包不包含模块。
Python包只是一个包含__init__.py
文件的文件夹。模块是包中(或直接在PYTHONPATH
上)具有.py
扩展名的任何其他文件。因此,在您的示例中,您有两个包,但没有定义模块。如果您认为某个包是一个文件系统文件夹而一个模块是文件,那么您就会看到为什么包中包含模块而不是相反的方式。
因此,在您的示例中假设Package-1和Package-2是您放在Python路径上的文件系统上的文件夹,您可以拥有以下内容:
Package-1/
namespace/
__init__.py
module1.py
Package-2/
namespace/
__init__.py
module2.py
您现在有一个包namespace
,包含两个模块module1
和module2
。除非你有充分的理由,你应该将模块放在文件夹中,并且只在下面的python路径中使用:
Package-1/
namespace/
__init__.py
module1.py
module2.py