C API 的稳定性

除非另有文档说明,Python 的 C API 将遵循 PEP 387 所描述的向下兼容策略。 对它的大部分改变都是源代码级兼容的(通常只会增加新的 new API)。 改变现有 API 或移除 API 只会在弃用期结束之后或需修复严重问题时才会发生。

CPython 的应用程序二进制接口(ABI)可以跨微版本向上和向下兼容(在以相同方式编译的情况下,参见下文 平台的考虑 一节)。 因此,针对 Python 3.10.0 编译的代码将适用于 3.10.8,反之亦然,但对于 3.9.x 和 3.11.x 则需要单独编译。

存在具有不同稳定性预期的两个 C API 层次:

  • 不稳定 API,可能在次要版本中发生改变而没有弃用期。 它的名称会以 PyUnstable 前缀来标记。

  • 受限 API,将会在多个次要版本间保持兼容。 当定义了 Py_LIMITED_API 时,将只有这个子集会从 Python.h 对外公开。

这些将在下文中更详细地讨论。

带有一个下划线前缀的名称,如 _Py_InternalState,是可能不经通知就改变甚至是在补丁发布版中改变的私有 API。 如果你需要使用这样的 API,请考虑联系 CPython 开发团队 来讨论为你的应用场景添加公有 API。

不稳定 C API

任何名称带有 PyUnstable 前缀的 API 都将对外公开 CPython 的实现细节,并可能不加弃用警告即在次要版本中发生改变(例如从 3.9 到 3.10)。 但是,它不会在问题修正发布版中改变(例如从 3.10.0 到 3.10.1)。

它通常是针对专门的,低层级的工具如调试器等。

使用此 API 的项目需要跟随 CPython 开发进程并花费额外的努力来适应改变。

应用程序二进制接口的稳定版

简单起见,本文档只讨论了 扩展,但受限 API 和稳定 ABI 对于 API 的所有用法都能发挥相同的作用 – 例如嵌入版的 Python 等。

受限 C API

Python 3.2 引入了 受限 API,它是 Python 的 C API 的一个子集。 只使用受限 API 扩展可以一次编译即适用于多个 Python 版本。 受限 API 的内容 如下所示

Py_LIMITED_API

请在包括 Python.h 之前定义这个宏以选择只使用受限 API,并选择受限 API 的版本。

Py_LIMITED_API 定义为与你的扩展所支持的最低 Python 版本的 PY_VERSION_HEX 的值。 扩展将无需重编译即可适用于从该指定版本开始的所有 Python 3 发布版,并可使用到该版本为止所引入的受限 API。

不直接使用 PY_VERSION_HEX 宏,而是碍编码一个最小的次要版本(例如 0x030A0000 表示 Python 3.10)以便在使用未来的 Python 版本进行编译时保持稳定。

你还可以将 Py_LIMITED_API 定义为 3。 其效果与 0x03020000 相同(即 Python 3.2,引入受限 API 的版本)。

稳定 ABI

为启用此特性,Python 提供了一个 稳定 ABI: 将能跨 Python 3.x 版本保持兼容的一组符号。

稳定 ABI 包含在 受限 API 中对外公开的符号,但还包含其他符号 – 例如,为支持旧版本受限 API 所需的函数。

在 Windows 上,使用稳定 ABI 的扩展应当被链接到 python3.dll 而不是版本专属的库如 python39.dll

在某些平台上,Python 将查找并载入名称中带有 abi3 标签的共享库文件 (例如 mymodule.abi3.so)。 它不会检查这样的扩展是否兼容稳定 ABI。 使用方 (或其打包工具) 需要确保这一些,例如,基于 3.10+ 受限 API 编译的扩展不可被安装于更低版本的 Python 中。

稳定 ABI 中的所有函数都会作为 Python 的共享库中的函数存在,而不仅是作为宏。 这使得它们可以在不使用 C 预处理器的语言中使用。

受限 API 的作用域和性能

受限 API 的目标是允许使用在完整 C API 中可用的任何东西,但可能会有性能上的损失。

例如,虽然 PyList_GetItem() 是可用的,但其 “不安全的” 宏版本 PyList_GET_ITEM() 则是不可用的。 这个宏的运行速度更快因为它可以利用版本专属的列表对象实现细节。

在未定义 Py_LIMITED_API 的情况下,某些 C API 函数将由宏来执行内联或替换。 定义 Py_LIMITED_API 会禁用这样的内联,允许提升 Python 的数据结构稳定性,但有可能降低性能。

通过省略 Py_LIMITED_API 定义,可以使基于版本专属的 ABI 来编译受限 API 扩展成为可能。 这能提升其在相应 Python 版本上的性能,但也将限制其兼容性。 基于 Py_LIMITED_API 进行编译将产生一个可在版本专属扩展不可用的场合分发的扩展 – 例如,针对即将发布的 Python 版本的预发布包。

受限 API 警示

请注意使用 Py_LIMITED_API 进行编译 无法 完全保证代码能够兼容 受限 API稳定 ABIPy_LIMITED_API 仅仅涵盖定义部分,但一个 API 还包括其他因素,如预期的语义等。

Py_LIMITED_API 不能处理的一个问题是附带在较低 Python 版本中无效的参数调用某个函数。 例如,考虑一个接受 NULL 作为参数的函数。 在 Python 3.9 中,NULL 现在会选择一个默认行为,但在 Python 3.8 中,该参数将被直接使用,导致一个 NULL 引用被崩溃。 类似的参数也适用于结构体的字段。

另一个问题是当定义了 Py_LIMITED_API 时某些结构体字段目前不会被隐藏,即使它们是受限 API 的一部分。

出于这些原因,我们建议用要支持的 所有 Python 小版本号来测试一个扩展,并最好是用其中 最低 的版本来编译它。

我们还建议查看所使用 API 的全部文档以检查其是否显式指明为受限 API 的一部分。 即使定义了 Py_LIMITED_API,少数私有声明还是会出于技术原因(或者甚至是作为程序缺陷在无意中)被暴露出来。

还要注意受限 API 并不必然是稳定的:在 Python 3.8 上用 Py_LIMITED_API 编译扩展意味着该扩展能在 Python 3.12 上运行,但它将不一定能用 Python 3.12 编译。 特别地,在稳定 ABI 保持稳定的情况下,部分受限 API 可能会被弃用并被移除。

平台的考虑

ABI 的稳定性不仅取决于 Python,取决于所使用的编译器、低层级库和编译器选项等。 对于 稳定 ABI 的目标来说,这些细节定义了一个 “平台”。 它们通常会依赖于 OS 类型和处理器架构等。

确保在特定平台上的所有 Python 版本都以不破坏稳定 ABI 的方式构建是每个特定 Python 分发方的责任。 来自 python.org 以及许多第三方分发商的 Windows 和 macOS 发布版都必于这种情况。

受限 API 的内容

目前 受限 API 包括下面这些项: