PEP 624 – 移除 Py_UNICODE 編碼器 API
- 作者:
- 稻田直樹 <songofacandy at gmail.com>
- 狀態:
- 最終版
- 型別:
- 標準跟蹤
- 建立日期:
- 2020年7月6日
- Python 版本:
- 3.11
- 釋出歷史:
- 2020年7月8日
摘要
本 PEP 提議在 Python 3.11 中移除已棄用的 Py_UNICODE 編碼器 API
PyUnicode_Encode()PyUnicode_EncodeASCII()PyUnicode_EncodeLatin1()PyUnicode_EncodeUTF7()PyUnicode_EncodeUTF8()PyUnicode_EncodeUTF16()PyUnicode_EncodeUTF32()PyUnicode_EncodeUnicodeEscape()PyUnicode_EncodeRawUnicodeEscape()PyUnicode_EncodeCharmap()PyUnicode_TranslateCharmap()PyUnicode_EncodeDecimal()PyUnicode_TransformDecimalToASCII()
注意
PEP 623 提議移除與 Py_UNICODE 相關的 Unicode 物件 API。另一方面,本 PEP 與 Unicode 物件無關。這些 PEP 之所以分開,是因為它們有不同的動機,需要不同的討論。
動機
總的來說,減少長期棄用且使用者較少的 API 數量是個好主意,因為它不僅提高了 CPython 的可維護性,還有助於 API 使用者和其他 Python 實現。
基本原理
自 Python 3.3 起已棄用
Py_UNICODE 及使用它的 API 自 Python 3.3 起已棄用。
效率低下
所有這些 API 都使用 PyUnicode_FromWideChar 實現。因此,當用戶想要編碼 Unicode 物件時,這些 API 效率低下。
使用不廣泛
在搜尋排名前 4000 的 PyPI 包 [1] 時,只有 pyodbc 使用這些 API。
PyUnicode_EncodeUTF8()PyUnicode_EncodeUTF16()
pyodbc 使用這些 API 將 Unicode 物件編碼為位元組物件。因此很容易修復。[2]
替代 API
存在接受 PyObject *unicode 而非 Py_UNICODE * 的替代 API。使用者可以遷移到它們。
| 已棄用 API | 替代 API |
|---|---|
PyUnicode_Encode() |
PyUnicode_AsEncodedString() |
PyUnicode_EncodeASCII() |
PyUnicode_AsASCIIString() (1) |
PyUnicode_EncodeLatin1() |
PyUnicode_AsLatin1String() (1) |
PyUnicode_EncodeUTF7() |
(2) |
PyUnicode_EncodeUTF8() |
PyUnicode_AsUTF8String() (1) |
PyUnicode_EncodeUTF16() |
PyUnicode_AsUTF16String() (3) |
PyUnicode_EncodeUTF32() |
PyUnicode_AsUTF32String() (3) |
PyUnicode_EncodeUnicodeEscape() |
PyUnicode_AsUnicodeEscapeString() |
PyUnicode_EncodeRawUnicodeEscape() |
PyUnicode_AsRawUnicodeEscapeString() |
PyUnicode_EncodeCharmap() |
PyUnicode_AsCharmapString() (1) |
PyUnicode_TranslateCharmap() |
PyUnicode_Translate() |
PyUnicode_EncodeDecimal() |
(4) |
PyUnicode_TransformDecimalToASCII() |
(4) |
備註
- 缺少
const char *errors引數。 - 沒有公共替代 API。但使用者可以使用通用的
PyUnicode_AsEncodedString()代替。 - 缺少
const char *errors, int byteorder引數。 - 沒有直接替代。但可以使用
Py_UNICODE_TODECIMAL代替。CPython 使用_PyUnicode_TransformDecimalAndSpaceToASCII進行 Unicode 到數字的轉換。
計劃
在 Python 3.11 中移除這些 API。它們已棄用。
PyUnicode_Encode()PyUnicode_EncodeASCII()PyUnicode_EncodeLatin1()PyUnicode_EncodeUTF7()PyUnicode_EncodeUTF8()PyUnicode_EncodeUTF16()PyUnicode_EncodeUTF32()PyUnicode_EncodeUnicodeEscape()PyUnicode_EncodeRawUnicodeEscape()PyUnicode_EncodeCharmap()PyUnicode_TranslateCharmap()PyUnicode_EncodeDecimal()PyUnicode_TransformDecimalToASCII()
替代方案
將 Py_UNICODE* 替換為 PyObject*
如“替代 API”部分所述,某些 API 沒有接受 PyObject *unicode 輸入的公共替代 API。並且某些公共替代 API 存在限制,例如缺少 errors 和 byteorder 引數。
我們可以重用已棄用 API 的名稱作為替代公共 API,而不是移除它們。
由於我們已有私有替代 API,因此只是將私有名稱重新命名為公共名稱和已棄用名稱。
| 重新命名為 | 重新命名自 |
|---|---|
PyUnicode_EncodeASCII() |
_PyUnicode_AsASCIIString() |
PyUnicode_EncodeLatin1() |
_PyUnicode_AsLatin1String() |
PyUnicode_EncodeUTF7() |
_PyUnicode_EncodeUTF7() |
PyUnicode_EncodeUTF8() |
_PyUnicode_AsUTF8String() |
PyUnicode_EncodeUTF16() |
_PyUnicode_EncodeUTF16() |
PyUnicode_EncodeUTF32() |
_PyUnicode_EncodeUTF32() |
優點
- 我們擁有更一致的 API 集。
缺點
- 向後不相容。
- 我們需要為不常用的情況維護更多公共 API。
- 現有的公共 API 足以滿足大多數用例,
PyUnicode_AsEncodedString()可用於其他情況。
將 Py_UNICODE* 替換為 Py_UCS4*
我們可以將 Py_UNICODE 替換為 Py_UCS4 並取消棄用這些 API。
UTF-8、UTF-16、UTF-32 編碼器內部支援 Py_UCS4。因此 PyUnicode_EncodeUTF8()、PyUnicode_EncodeUTF16() 和 PyUnicode_EncodeUTF32() 可以避免建立臨時 Unicode 物件。
優點
- 當使用 UTF-8、UTF-16、UTF-32 編解碼器將
Py_UCS4*編碼為位元組物件時,我們可以避免建立臨時 Unicode 物件。
缺點
- 向後不相容。
- 我們需要為不常用的情況維護更多公共 API。
- 其他希望支援 Python/C API 的 Python 實現也需要支援這些 API。
- 如果我們將來將 Unicode 內部表示更改為 UTF-8,我們只需要為這些 API 保留 UCS-4 支援。
將 Py_UNICODE* 替換為 wchar_t*
我們可以將 Py_UNICODE 替換為 wchar_t。由於 Py_UNICODE 已經是 wchar_t 的 typedef,所以這是現狀。
在 sizeof(wchar_t) == 4 的平臺上,當使用 UTF-8、UTF-16 和 UTF-32 編解碼器將 wchar_t* 編碼為位元組物件時,我們可以避免建立臨時 Unicode 物件,就像“將 Py_UNICODE* 替換為 Py_UCS4*”的思路一樣。
優點
- 向後相容。
- 在
sizeof(wchar_t) == 4的平臺上,當使用 UTF-8、UTF-16、UTF-32 編解碼器將Py_UCS4*編碼為位元組物件時,我們可以避免建立臨時 Unicode 物件。
缺點
- 儘管 Windows 是大量使用
wchar_t的主要平臺,但這些 API 總是需要建立臨時 Unicode 物件,因為 Windows 上sizeof(wchar_t) == 2。 - 我們需要為不常用的情況維護更多公共 API。
- 其他希望支援 Python/C API 的 Python 實現也需要支援這些 API。
- 如果我們將來將 Unicode 內部表示更改為 UTF-8,我們只需要為這些 API 保留 UCS-4 支援。
被拒絕的想法
發出執行時警告
除了現有的編譯器警告,建議發出執行時 DeprecationWarning。
但這些 API 目前不釋放 GIL。從這樣的 API 發出警告是不安全的。請看這個例子。
PyObject *u = PyList_GET_ITEM(list, i); // u is borrowed reference.
PyObject *b = PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(u),
PyUnicode_GET_SIZE(u), NULL);
// Assumes u is still living reference.
PyObject *t = PyTuple_Pack(2, u, b);
Py_DECREF(b);
return t;
如果我們從 PyUnicode_EncodeUTF8() 發出 Python 警告,警告過濾器和其他執行緒可能會改變 list,並且在 PyUnicode_EncodeUTF8() 返回後,u 可能成為懸空引用。
討論
- [python-dev] 計劃移除 PEP 623 之外的 Py_UNICODE API
- bpo-41123: 移除 PEP 623 之外的 Py_UNICODE API
- [python-dev] PEP 624: 移除 Py_UNICODE 編碼器 API
反對意見
- 移除這些 API 會失去無需臨時 Unicode 即可使用編解碼器的能力。
- 自 Python 3.3 起,編解碼器無法直接編碼 Unicode 緩衝區而無需臨時 Unicode 物件。所有這些 API 目前都會建立臨時 Unicode 物件。因此,移除它們不會降低任何能力。
- 為什麼不也移除解碼器 API?
- 它們是穩定 ABI 的一部分。
PyUnicode_DecodeASCII()和PyUnicode_DecodeUTF8()被廣泛使用。棄用它們不值得。- 解碼器 API 可以直接從位元組緩衝區解碼,而無需建立臨時位元組物件。另一方面,編碼器 API 無法避免臨時 Unicode 物件。
參考資料
版權
本文件置於公共領域或 CC0-1.0-Universal 許可證下,以更寬鬆者為準。
來源: https://github.com/python/peps/blob/main/peps/pep-0624.rst
最後修改: 2025-02-01 08:55:40 GMT