代码之家  ›  专栏  ›  技术社区  ›  joeforker

如何在python中创建名称空间包?

  •  119
  • joeforker  · 技术社区  · 15 年前

    在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产品可以在该名称空间中定义模块?

    5 回复  |  直到 6 年前
        1
  •  55
  •   clacke    10 年前

    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 包含一个 π介子 文件
    • 由一个或多个目录表示的命名空间包 没有任何 π介子 文件夹

    包也是模块,但这里我说“模块”时是指“非包模块”。

    首先扫描 sys.path 用于模块或常规包。如果成功,它将停止搜索并创建和初始化模块或包。如果找不到模块或常规包,但至少找到一个目录,则创建并初始化命名空间包。

    模块和常规包 __file__ 设置为 .py 从中创建的文件。常规和命名空间包具有 __path__ 设置为从中创建的一个或多个目录。

    当你这样做的时候 import foo.bar ,以上搜索首先发生在 ,然后如果找到包,则搜索 bar 完成了 foo.__path__ 作为搜索路径而不是 搜索路径 . 如果 foo.bar 找到了, 酒吧酒吧 创建并初始化。

    那么常规包和命名空间包是如何混合的呢?通常不会,但老的 pkgutil 显式命名空间包方法已扩展为包括隐式命名空间包。

    如果现有的常规包 π介子 这样地:

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

    …遗留行为是添加任何其他 有规律的 在其搜索路径上的包 阿尔法 . 但在Python3.3中,它还添加了名称空间包。

    因此,您可以具有以下目录结构:

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

    …只要两个 π介子 extend_path 线(和) path1 , path2 path3 在你的 搜索路径 ) import package.foo , import package.bar import package.baz 都会工作。

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

        2
  •  77
  •   Luis Masuelli    10 年前

    有一个标准模块,叫做 pkgutil ,和你一起 无法将模块“append”到给定的命名空间。

    使用您提供的目录结构:

    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 以包命名。

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

        3
  •  5
  •   Asclepius    12 年前

    This section should be pretty self-explanatory.

    简而言之,将名称空间代码放入 __init__.py 更新 setup.py 声明一个名称空间,您就可以自由使用了。

        4
  •  2
  •   Lars Francke    6 年前

    这是一个古老的问题,但最近有人在我的博客上评论说,我关于命名空间包的文章仍然是相关的,所以我认为我会链接到它,因为它提供了一个如何实现它的实用示例:

    https://web.archive.org/web/20150425043954/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 到目前为止,似乎正在解决问题。

        5
  •  -8
  •   Tendayi Mawushe    15 年前

    您的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