C API 的稳定性

Python 的 C 语言 API 包含于向下兼容政策 PEP 387 中。C API 会跟随小版本的发布而发生变化(比如 3.9 到 3.10 的时候),不过大多数变化都是源代码级兼容的,通常只会增加新的 API。已有 API 的修改或删除,只有在废止期过后或修复严重问题时才会进行。

CPython 的应用二进制接口(ABI)可以跨小版本实现前后兼容(只要以同样方式编译;参见下面的 平台的考虑 )。因此,用 Python 3.10.0 编译的代码可以在 3.10.8 上运行,反之亦然,但针对 3.9.x 和 3.10.x 则需分别进行编译。

带下划线前缀的是私有 API,如 _Py_InternalState,即便是补丁发布版本中也可能不加通知地进行改动。

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

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

为了实现这一点,Python 提供了一个 稳定 ABI: 一个将在各 Python 3.x 版本中保持兼容性的符号集合。 稳定 ABI 包含了在受限 API 中暴露的符号,但还包含其他符号 – 例如,支持旧版受限 API 所需的函数。

(简单起见,本文档只讨论了 扩展,但受限 API 和稳定 ABI 对于 API 的所有用法都同样适用 – 例如,嵌入 Python 等。)

Py_LIMITED_API

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

Define Py_LIMITED_API to the value of PY_VERSION_HEX corresponding to the lowest Python version your extension supports. The extension will work without recompilation with all Python 3 releases from the specified one onward, and can use Limited API introduced up to that version.

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

你还可以将 Py_LIMITED_API 定义为 3。 其效果与 0x03020000 相同(即 Python 3.2,引入受限 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 或稳定 ABI。 Py_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 包括下面这些项: