如何在Python中创build一个名称空间包?

在Python中,命名空间包允许您在多个项目中传播Python代码。 当您想要将相关库作为单独的下载发布时,这非常有用。 例如,在PYTHONPATH的目录Package-1Package-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.module1import namespace.module2

定义一个名称空间包的最好方法是什么,以便多个Python产品可以在该名称空间中定义模块?

有一个标准的模块叫做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__.pyPackage-2/namespace/__init__.py (*)中:

 from pkgutil import extend_path __path__ = extend_path(__path__, __name__) 

(*因为-unless你声明他们之间的依赖关系 – 你不知道哪个会被首先识别 – 请参阅PEP 420了解更多信息)

正如文档所述 :

这将添加到程序包的__path__所有以该程序包命名的sys.path上的所有子目录。

从现在起,你应该能够独立分发这两个软件包。

TL; DR:

在Python 3.3中,你不需要做任何事情,只要不把任何__init__.py放到你的命名空间的软件包目录中,它就可以工作。 在3.3之前的版本中,selectpkgutil.extend_path()方法的pkgutil.extend_path()解决scheme,因为它具有前瞻性,并且已经与隐式名称空间包兼容。


Python 3.3引入了隐式名称空间包,参见PEP 420 。

这意味着现在有三种types的对象可以通过import foo创build:

  • foo.py文件表示的模块
  • 常规包,由包含__init__.py文件的目录foo表示
  • 名称空间包,由一个或多个目录foo表示,没有任何__init__.py文件

软件包也是模块,但是在我说“模块”的时候,我的意思是“非软件包模块”。

首先它扫描一个模块或常规软件包的sys.path 。 如果成功,则停止search并创build和初始化模块或包。 如果找不到模块或常规软件包,但至lessfind一个目录,则会创build并初始化一个名称空间包。

模块和常规软件包将__file__设置为从其创build的.py文件。 常规和名称空间包的__path__设置为__path__创build的目录。

当你import foo.bar ,上面的search首先发生在foo ,那么如果find了一个包,searchbar就是用foo.__path__作为searchpath而不是sys.path 。 如果findfoo.bar则会创build并初始化foofoo.bar

那么常规包和名称空间包怎么混合呢? 通常他们不这样做,但旧的pkgutil显式名称空间包方法已被扩展为包含隐式名称空间包。

如果你有一个现有的常规软件包,像这样有一个__init__.py

 from pkgutil import extend_path __path__ = extend_path(__path__, __name__) 

…传统的行为是添加任何其他常规软件包searchpath到__path__ 。 但在Python 3.3中,它也添加了命名空间包。

所以你可以有以下目录结构:

 ├── path1 │  └── package │  ├── __init__.py │  └── foo.py ├── path2 │  └── package │  └── bar.py └── path3 └── package ├── __init__.py └── baz.py 

…只要两个__init__.py具有extend_path行(并且path1path2path3在你的sys.pathimport package.fooimport package.barimport package.baz都将工作。

pkg_resources.declare_namespace(__name__)尚未更新为包含隐式名称空间包。

这部分应该是不言自明的。

简而言之,将名称空间代码放在__init__.py ,更新setup.py来声明一个名称空间,然后就可以自由地进行了。

这是一个古老的问题,但最近有人在我的博客上评论说,我关于命名空间包的发布仍然是相关的,所以我想在这里链接到它,因为它提供了一个实际的例子:

http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

那链接到这篇文章为什么发生的主要胆量:

http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package

__import__("pkg_resources").declare_namespace(__name__)技巧几乎是驱动TiddlyWeb中插件的pipe理,因此似乎正在努力。

你有你的Python命名空间的概念,python不可能将包放入模块。 包不包含模块。

Python包只是一个包含__init__.py文件的文件夹。 模块是包中的任何其他文件(或直接在PYTHONPATH ),其扩展名为.py 。 所以在你的例子中你有两个包但没有定义模块。 如果你认为一个软件包是一个文件系统文件夹,而一个模块是文件,那么你就会明白为什么软件包包含模块,而不是相反。

因此,在您的示例中,假设Package-1和Package-2是放在Pythonpath上的文件系统上的文件夹,您可以使用以下命令:

 Package-1/ namespace/ __init__.py module1.py Package-2/ namespace/ __init__.py module2.py 

现在您有一个包含两个模块module1module2namespace 。 除非你有一个很好的理由,否则你应该把这些模块放在这个文件夹中,并且只有像下面这样的pythonpath:

 Package-1/ namespace/ __init__.py module1.py module2.py