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 分別編譯。
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 版本相容 ABI,並且可以使用在該版本之前引入的有限 API。不要直接使用
PY_VERSION_HEX
宏,而是硬編碼一個最小次要版本(例如,Python 3.10 的0x030A0000
),以便在未來 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或穩定 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 **編譯**。特別是,有限 API 的某些部分可能會被棄用和刪除,前提是穩定 ABI 保持穩定。
平臺注意事項¶
ABI 穩定性不僅取決於 Python,還取決於所使用的編譯器、低階庫和編譯器選項。就穩定 ABI而言,這些細節定義了一個“平臺”。它們通常取決於作業系統型別和處理器架構。
每個 Python 分發商都有責任確保在特定平臺上構建所有 Python 版本的方式不會破壞穩定 ABI。 python.org
的 Windows 和 macOS 版本以及許多第三方分發商都符合這種情況。
有限 API 的內容¶
目前,有限 API 包括以下專案:
PyByteArrayIter_Type
PyBytesIter_Type
PyBytes_DecodeEscape()
PyBytes_Repr()
PyCFunction_GetFlags()
PyCFunction_GetFunction()
PyCFunction_GetSelf()
PyCFunction_Type
PyCapsule_Type
PyClassMethodDescr_Type
PyDictItems_Type
PyDictIterItem_Type
PyDictIterKey_Type
PyDictIterValue_Type
PyDictKeys_Type
PyDictProxy_Type
PyDictRevIterItem_Type
PyDictRevIterKey_Type
PyDictRevIterValue_Type
PyDictValues_Type
PyEnum_Type
PyErr_Display()
PyErr_ProgramText()
PyFilter_Type
PyGetSetDescr_Type
PyListIter_Type
PyListRevIter_Type
PyLongRangeIter_Type
PyMap_Type
PyMemberDescr_Type
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Base
PyModuleDef_Type
PyOS_InterruptOccurred()
PyOS_mystricmp()
PyOS_mystrnicmp()
PyRangeIter_Type
PyRange_Type
PyReversed_Type
PySetIter_Type
PySuper_Type
PyThread_GetInfo()
PyThread_acquire_lock()
PyThread_acquire_lock_timed()
PyThread_allocate_lock()
PyThread_exit_thread()
PyThread_free_lock()
PyThread_get_stacksize()
PyThread_get_thread_ident()
PyThread_get_thread_native_id()
PyThread_init_thread()
PyThread_release_lock()
PyThread_set_stacksize()
PyThread_start_new_thread()
PyTraceBack_Here()
PyTraceBack_Print()
PyTraceBack_Type
PyTupleIter_Type
PyUnicode_AsDecodedObject()
PyUnicode_AsDecodedUnicode()
PyUnicode_AsEncodedObject()
PyUnicode_AsEncodedUnicode()
PyVarObject.ob_base
PyWeakReference
PyWrapperDescr_Type
PyZip_Type
Py_FileSystemDefaultEncodeErrors
Py_FileSystemDefaultEncoding
Py_GetRecursionLimit()
Py_HasFileSystemDefaultEncoding
Py_MakePendingCalls()
Py_SetRecursionLimit()
Py_UTF8Mode
Py_intptr_t
Py_uintptr_t
ssizessizeargfunc
ssizessizeobjargproc
symtable