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

时间:2023-04-17
本文介绍了如何在 Python 中创建命名空间包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

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

In Python, a namespace package allows you to spread Python code among several projects. This is useful when you want to release related libraries as separate downloads. For example, with the directories Package-1 and Package-2 in 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.module1import namespace.module2.

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

What's the best way to define a namespace package so more than one Python product can define modules in that namespace?

推荐答案

TL;DR:

在 Python 3.3 上,您无需执行任何操作,只需不要将任何 __init__.py 放入您的命名空间包目录中,它就会正常工作.在 3.3 之前,选择 pkgutil.extend_path() 解决方案而不是 pkg_resources.declare_namespace() 解决方案,因为它是面向未来的并且已经与隐式命名空间包兼容.

On Python 3.3 you don't have to do anything, just don't put any __init__.py in your namespace package directories and it will just work. On pre-3.3, choose the pkgutil.extend_path() solution over the pkg_resources.declare_namespace() one, because it's future-proof and already compatible with implicit namespace packages.

Python 3.3 引入了隐式命名空间包,请参阅 PEP 420.

Python 3.3 introduces implicit namespace packages, see PEP 420.

这意味着现在可以通过 import foo 创建三种类型的对象:

This means there are now three types of object that can be created by an import foo:

  • foo.py 文件表示的模块
  • 一个常规包,由包含 __init__.py 文件的目录 foo 表示
  • 一个命名空间包,由一个或多个目录 foo 表示,没有任何 __init__.py 文件
  • A module represented by a foo.py file
  • A regular package, represented by a directory foo containing an __init__.py file
  • A namespace package, represented by one or more directories foo without any __init__.py files

包也是模块,但这里我说的模块"是指非包模块".

Packages are modules too, but here I mean "non-package module" when I say "module".

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

First it scans sys.path for a module or regular package. If it succeeds, it stops searching and creates and initalizes the module or package. If it found no module or regular package, but it found at least one directory, it creates and initializes a namespace package.

模块和常规包将 __file__ 设置为创建它们的 .py 文件.常规和命名空间包的 __path__设置为创建它们的目录.

Modules and regular packages have __file__ set to the .py file they were created from. Regular and namespace packages have __path__set to the directory or directories they were created from.

当你执行 import foo.bar 时,上面的搜索首先发生在 foo,然后如果找到一个包,则搜索 bar 使用 foo.__path__ 作为搜索路径而不是 sys.path.如果找到 foo.bar,则创建并初始化 foofoo.bar.

When you do import foo.bar, the above search happens first for foo, then if a package was found, the search for bar is done with foo.__path__as the search path instead of sys.path. If foo.bar is found, foo and foo.bar are created and initialized.

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

So how do regular packages and namespace packages mix? Normally they don't, but the old pkgutil explicit namespace package method has been extended to include implicit namespace packages.

如果您有一个现有的常规包,其中包含这样的 __init__.py:

If you have an existing regular package that has an __init__.py like this:

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

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

... the legacy behavior is to add any other regular packages on the searched path to its __path__. But in Python 3.3, it also adds namespace packages.

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

So you can have the following directory structure:

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

...只要两个 __init__.pyextend_path 行(和 path1path2path3 在你的 sys.path) import package.foo, import package.barimport package.baz 一切正常.

... and as long as the two __init__.py have the extend_path lines (and path1, path2 and path3 are in your sys.path) import package.foo, import package.bar and import package.baz will all work.

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

pkg_resources.declare_namespace(__name__) has not been updated to include implicit namespace packages.

这篇关于如何在 Python 中创建命名空间包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:兄弟包导入 下一篇:什么是 Python 蛋?

相关文章