C API 穩定性

除非有另外記錄於文件,Python 的 C API 被包含在向後相容性策略 PEP 387 中。大多數改動都是相容於原始碼的(通常只會增加新的 API)。更改現有 API 或刪除 API 僅在棄用期後或修復嚴重問題時進行。

CPython 的應用程式二進位介面 (Application Binary Interface, ABI) 在次要版本中是向前和向後相容的(如果它們以相同的方式編譯;請參閱下面的平台注意事項)。因此,為 Python 3.10.0 編譯的程式碼將能夠在 3.10.8 上運行,反之亦然,但 3.9.x 和 3.10.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 开发进程并花费额外的努力来适应改变。

穩定的應用程式二進位介面

為簡單起見,本文件討論擴充 (extension),但受限 API 和穩定 ABI 在所有 API 使用方式中都以相同的方式運作 -- 例如在嵌入式 Python (embedding Python) 中。

受限 C API

Python 3.2 引入了受限 API (Limited 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 巨集,不如寫死 (hardcode) 最小次要版本(例如代表 Python 3.10 的 0x030A0000),以便在使用未來的 Python 版本進行編譯時仍保持穩定性。

你還可以將 Py_LIMITED_API 定義為 3,這與 0x03020000(Python 3.2,引入了受限 API 的版本)相同。

穩定 ABI

為了實現它,Python 提供了一個穩定 ABI (Stable 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 預處理器 (preprocessor) 的語言。

受限 API 範圍和性能

受限 API 的目標是允許使用完整的 C API 進行所有可能的操作,但可能會降低性能。

例如,雖然 PyList_GetItem() 可用,但它的「不安全」巨集變體 PyList_GET_ITEM() 為不可用。巨集運行可以更快,因為它可以依賴 list 物件的特定版本實作細節。

如果沒有定義 Py_LIMITED_API,一些 C API 函式將被嵌入或被替換為巨集。定義 Py_LIMITED_API 會禁用嵌入,從而隨著 Python 資料結構的改進而提高穩定性,但可能會降低性能。

通過省略 Py_LIMITED_API 定義,可以使用特定版本的 ABI 編譯受限 API 擴充。這可以提高該 Python 版本的性能,但會限制相容性。使用 Py_LIMITED_API 編譯將產生一個擴充,可以在特定版本的擴充不可用的地方發布 — 例如,用於即將發布的 Python 版本的預發布版本 (prerelease)。

受限 API 注意事項

請注意,使用 Py_LIMITED_API 進行編譯完全保證程式碼符合受限 API穩定 ABIPy_LIMITED_API 僅涵蓋定義,但 API 還包括其他議題,例如預期的語義 (semantic)。

Py_LIMITED_API 無法防範的一個問題是使用在較低 Python 版本中無效的引數來呼叫函式。例如一個開始接受 NULL 作為引數的函式。在 Python 3.9 中,NULL 現在代表選擇預設行為,但在 Python 3.8 中,引數將被直接使用,導致 NULL 取消參照 (dereference) 且崩潰 (crash)。類似的引數適用於結構 (struct) 的欄位。

另一個問題是,當有定義 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 的目的,這些細節定義了一個「平台」。它們通常取決於作業系統種類和處理器架構

每個特定的 Python 發布者都有責任確保特定平台上的所有 Python 版本都以不破壞穩定 ABI 的方式建置。python.org 和許多第三方發布者發布的 Windows 和 macOS 版本就是這種情況。

受限 API 的內容

目前,受限 API 包括以下項目: