C API 的稳定性

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

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

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

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

简单起见,本文档只讨论了 扩展,但受限 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 发布版保持 ABI 兼容,并可使用到该版本为止所引入的受限 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 兼容的符号集合。

备注

稳定 ABI 将防止多种 ABI 问题,如由于缺失符号导致的链接器错误或由于结构体布局或函数签名中的变化导致的数据损坏。不过,Python 中的其他修改可能改变扩展的 行为。请参阅 Python 的向下兼容策略 (PEP 387) 了解详情。

稳定 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 可能会被弃用并被移除。