Python 命名空间包及其使用方法?

2024 年 08 月 29 日 | 阅读 9 分钟

在本教程中,我们将探讨 Python 命名空间包及其在 Python 编程中的重要性。命名空间包是与包中常用的 __init__.py 文件相关的 Python 高级功能。如果一个包没有 __init__.py 文件,它就成为一个命名空间包。我们将深入研究这个主题,以了解其重要性和功能。

总的来说,命名空间包与常规包在项目中的使用方式差异不大。我们仍然可能需要在包中包含一个 __init__.py 文件而不会发现任何问题。虽然与标准包相比,命名空间包的导入速度可能略慢,但不太可能遇到任何重大问题。

Python 命名空间包主要惠及那些管理或设计相互关联包的个人。但是,我们可以通过一个项目让命名空间包对普通用户更友好。

这是一个高级主题,因此您应该熟悉 Python 的基本概念和导入系统,并对打包有所了解。让我们了解什么是 Python 命名空间包以及它的用途。

什么是 Python 命名空间包?

在理解命名空间包之前,让我们回顾一下 Python 中命名空间的概念。

"在 Python 中,命名空间是包含在特定上下文(例如模块、类或函数)中定义的名称的容器。它们充当组织和区分程序中名称的机制,防止命名冲突并允许在不同上下文中重用名称。可以使用点表示法访问命名空间,其中命名空间名称后跟命名空间中对象的名称。"

例如 - 当我们导入 requests 时,我们可以访问 requests 命名空间并从许多不同的对象中进行选择。

我们还可以将 Python 字典称为命名空间。我们可以取两个最初完全独立、不相关的变量,将它们与 Python 字典一起包含在同一个字典命名空间中。让我们看下面的例子。

示例 -

解释 -

上面的代码片段引用了来自 sample_python 命名空间的 home_page 和 import_tutorial 值。命名空间包的工作方式类似,只是它们将整个包而不是单个值或其他 Python 对象分组。

命名空间包允许 PyPI 上的两个包共享相同的命名空间。拥有多个命名空间包可能更实用。命名空间包的真正好处在您拥有至少两个包时才会显现。

企业通常在管理希望维护在公司命名空间下的大量包时使用命名空间包。例如,Microsoft Azure 包安装后可以在 azure 命名空间下访问。

在下一节中,我们将创建一个公司命名空间作为示例。

如何创建命名空间包?

假设您在 Apex Corporation 工作,您的团队需要一个包含所有库的命名空间包 apex_corp。因此,无论您使用哪个包,只要它是 Apex Corporation 开发的,您都将从 apex_corp 导入它。

在没有命名空间包的情况下,您有两种选择

  • 开发一个包含单个包 apex_corp 的单体仓库,其中包含您所需的所有不同库和实用程序的众多模块。
  • 开发多个包,但每个包都必须以 apex_corp 为前缀。例如,您可能有一个名为 apex_corp_dateutil 的包。

开发单体仓库的问题在于,它迫使每个人都下载所有代码,即使他们只使用了其中的一小部分。它还会使版本控制和其他打包工作流复杂化,特别是如果不同的团队负责子包。

创建具有共享前缀的多个包的问题在于它可能变得冗长、混乱和不美观。此外,Apex Corporation 的首席执行官对此解决方案表示不满,并更喜欢单体仓库替代方案而不是包前缀。此外,使用共享前缀只是一种约定,并未建立真正的公共命名空间。

这种情况为使用命名空间包提供了绝佳的机会。通过命名空间包,我们可以拥有许多具有独特打包工作流的不同包,但它们都可以存在于相同的 snake_corp 命名空间下。

以下是如何实现命名空间包创建的说明。我们将创建三个包 -

  • apex-corp-dateutil - 它包含一些 Apex Corporation 特有的日期实用程序。
  • apex-corp-magic-numbers - 它包含一些 Apex Corporation 特有的数字生成实用程序。
  • apex-service - 一个希望同时使用 apex-corp-dateutil 和 apex-corp-magic-numbers 包的员工项目。

在下一节中,我们将设置这些包并将它们安装在我们的虚拟环境中。

设置一些命名空间包

Apex 公司的命名空间包通过创建以下文件夹和文件结构 -

文件结构包含三个包,并且两个实用程序包(即 apex-corp-dateutil 和 apex-corp-magic-numbers)都通过定义一个名为 apex_corp 的隐式命名空间包开始。由于没有 init.py 文件,它被认为是一个隐式命名空间包。

至于导入名称,我们将把 apex-corp-dateutil 包导入为 apex_corp.dateutil,将 apex-corp-magic-numbers 导入为 apex_corp.magic。

为了包含项目,我们将使用虚拟环境。我们将在虚拟环境中安装两个实用程序包,因此每个包都有一个 pyproject.toml 文件。这模拟了包可以在公司包服务器上访问的场景。

在 dateutil.py 模块中,我们只需要包含一个业务关键功能,它将返回到 Apex 基金会日期的天数。

示例 -

相关的 pyproject.toml 文件包含 setuptools 正确安装包所需的信息。

示例 -

以上文件确保 pip 知道如何处理这个包。

tool.setuptools.packages.find 部分主要控制包发现,命名空间包通常默认启用。但是,我们可以通过指定 where、include 和 namespaces 等选项来更具体地包含您的命名空间包。

在 magic.py 模块中,我们还可以包含一个某些 Apex Corporation 官僚机构所需的数字生成器。请注意,此数字生成器在此示例中纯粹是一个占位符。

magic.py 模块中的函数返回一个随机数,在此示例中没有实际用途。然而,在实际场景中,此函数可以生成某些任务可能需要的特殊令牌,例如访问企业 API 服务或生成唯一标识符。

相应的 pyproject.toml 文件与上一个几乎相同,只修改了包名称、版本和描述。

现在,我们准备在虚拟环境中安装 pip。在接下来的部分中,我们将学习开发新包并同时利用我们刚刚创建的命名空间包的过程。

安装和使用命名空间包

现在,导航到外部的 apex-corp 文件夹,在那里我们可以看到两个实用程序包的文件夹。我们将在那里创建虚拟环境,并安装这些包。

在此示例中,包安装期间包含了 -e 标志,该标志允许可编辑安装。此功能允许更改已安装包的大部分源代码而无需重新安装。但是,请务必注意,此标志通常仅在您正在开发包并积极更改源代码时才需要。

我们已正确安装所需的包并激活了虚拟环境,运行此模块应该会在控制台上打印出两个数字而不会出现任何异常。打印的数字可能与此模块中显示的数字不同,因为它们取决于模块使用的输入数据。

最初,隐式命名空间包可能看起来不熟悉。拥有一个指定文件来指示一个包应该被视为命名空间包会更直接。与常规包相比,创建命名空间包相对容易。这些担忧是有效的,并将在下一节中解决。

命名空间包为何存在?

Python 命名空间包在以下两种场景中很有用

  • 当您有一个大型包,并非所有用户都需要访问它提供的所有组件时。在这种情况下,命名空间包允许我们维护一个公共命名空间,同时只选择性地导入所需的组件。
  • 当您有多个独立但相互关联的包,并且希望它们共享相同的命名空间时。在这里,命名空间包可以将这些包分组在一个命名空间下,从而便于使用和组织。

理想情况下,我们希望将包安装在不同的位置,并且它们仍然可以在我们的代码中作为内聚包运行。此外,我们更喜欢这些包相对独立,因此拥有多个包构成一个命名空间不需要安装所有这些包才能开始使用该命名空间。

在 Python 中建立公共命名空间的传统方法是使用 pkgutil 模块。尽管现在它被认为已过时,但在现代 Python 中仍然可以使用此方法。使用 pkgutil 创建命名空间包涉及在所有命名空间包的 __init__.py 文件中添加以下代码片段 -

示例 -

隐式命名空间包受到了一些批评。目前,创建命名空间包比创建常规包更容易。这可能会让初学者感到困惑,他们认为命名空间包是默认选项,并且可能觉得有必要通过为其包创建 __init__.py 文件来选择退出。

PEP 420 概述了 Python 标准库可以从命名空间包中受益的场景。具体来说,标准库中的 encodings 模块有可能被转换为命名空间包。这样做将使可能需要晦涩编码的各种操作系统能够轻松地将它们的编码添加到 encodings 命名空间中。

命名空间包示例

尽管不常见,但命名空间包确实存在于公共领域。以下是一些采用命名空间包的公共项目示例

  • OpenTelemetry
  • discord.py
  • Azure
  • Google

最初,我们将探讨将命名空间包上传到 PyPI 后如何安装。随后,我们将检查其源代码以了解其内部工作原理。最后,我们将深入研究如何扩展命名空间包以建立原型插件系统。

结论

在本教程中,我们讨论了命名空间包的功能以及何时以及如何使用它们。我们还探讨了如何为组织创建命名空间包。即使您尚未获得命名空间包的经验,本教程也让您了解了它们是什么以及何时有用。因此,下次您包含一个空的 init.py 文件时,您将更好地理解其目的。