如何在Python中创建命名空间包?

joe*_*ker 132 python namespaces package

在Python中,命名空间包允许您在多个项目中传播Python代码.当您想要将相关库作为单独的下载发布时,这非常有用.例如,使用目录Package-1Package-2in PYTHONPATH,

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py
Run Code Online (Sandbox Code Playgroud)

最终用户可以import namespace.module1import namespace.module2.

定义命名空间包的最佳方法是什么,因此多个Python产品可以在该命名空间中定义模块?

Mik*_*cki 79

有一个名为pkgutil的标准模块,您可以使用该模块将模块"附加"到给定的命名空间.

使用您提供的目录结构:

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py
Run Code Online (Sandbox Code Playgroud)

你应该把这两行放在两个Package-1/namespace/__init__.pyPackage-2/namespace/__init__.py(*)中:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Run Code Online (Sandbox Code Playgroud)

(*因为 - 你没有说明他们之间的依赖关系 - 你不知道他们中的哪一个将被首先识别 - 有关更多信息,请参阅PEP 420)

正如文件所说:

这将添加到软件包的__path__所有目录子目录中sys.path.

从现在开始,您应该能够独立分发这两个包.

  • 使用它与__import __('pkg_resources')的优缺点是什么.declare_namespace(__ name__)? (17认同)
  • 首先,在这种情况下,`__ import__`被认为是坏样式,因为它可以很容易地用普通的import语句替换.更重要的是,pkg_resources是一个非标准库.它配备了setuptools,但这不是问题.快速谷歌搜索显示pkgutil是在2.5中引入的,pkg_resources早于它.然而,pkgutil是一种经过公认的解决方案.事实上,pkg_resources包含在PEP 365中被拒绝. (14认同)
  • 不应将这两行放入两个文件:`Package-1/namespace/__ init __.py`*和*`Package-2/namespace/__ init __.py`,条件是我们不知道首先列出哪个Package目录? (7认同)
  • 引自[PEP 382](http://www.python.org/dev/peps/pep-0382/):命名空间包的当前命令式方法导致了多个稍微不兼容的机制来提供命名空间包.例如,pkgutil支持*.pkg文件; setuptools没有.同样,setuptools支持检查zip文件,并支持向_namespace_packages变量添加部分,而pkgutil则不支持. (3认同)
  • @ChristofferKarlsson是的,重要的是,如果你知道哪个是第一个就没关系,但真正的问题是你可以保证它会在任何情况下成为第一个,即对于其他用户吗? (3认同)
  • @Bula:你是对的.从[PEP 420](http://www.python.org/dev/peps/pep-0420/):"每个发行版都需要在其`__init __.py`中提供相同的内容,以便调用`extend_path`独立于首先导入包的哪个部分." (3认同)

cla*_*cke 69

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被发现,foofoo.bar创建和初始化.

那么常规包和命名空间包如何混合?通常它们不会,但旧的pkgutil显式命名空间包方法已扩展为包含隐式命名空间包.

如果您有一个__init__.py类似于此的现有常规包:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Run Code Online (Sandbox Code Playgroud)

...遗留行为是将搜索到的路径上的任何其他常规包添加到其中__path__.但是在Python 3.3中,它还添加了命名空间包.

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

??? path1
?   ??? package
?       ??? __init__.py
?       ??? foo.py
??? path2
?   ??? package
?       ??? bar.py
??? path3
    ??? package
        ??? __init__.py
        ??? baz.py
Run Code Online (Sandbox Code Playgroud)

......只要两个__init__.pyextend_path有线(和path1,path2并且path3在你的身上sys.path)import package.foo,import package.bar并且import package.baz都会有效.

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

  • 我应该在`setup.py`中添加`namespace_packages = ['package']`吗? (3认同)
  • 怎么样的setuptools?我必须使用`namespace_packages`选项吗?和[`__import __('pkg_resources').declare_namespace(__name__)`](http://setuptools.readthedocs.io/en/latest/setuptools.html#namespace-packages)的事情是什么? (2认同)
  • @cracke:使用 `namespace_packages=['package']`,setup.py 将在 EGG-INFO 中添加一个 ``namespace_packages.txt`` 。还是不知道影响... (2认同)

iEl*_*ric 5

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

简而言之,将命名空间代码放入__init__.py,更新setup.py以声明命名空间,您可以自由选择.

  • 如果相关链接失效,您应该始终引用链接的相关部分. (7认同)