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