PEP 624 – 移除 Py_UNICODE 编码器 API
- 作者:
- Inada Naoki <songofacandy at gmail.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2020-07-06
- Python 版本:
- 3.11
- 历史记录:
- 2020-07-08
摘要
本 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
存在替代 API,它们接受 PyObject *unicode
而不是 Py_UNICODE *
。用户可以迁移到它们。
已弃用的 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()
备选方案
用 PyObject*
替换 Py_UNICODE*
如“替代 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_UCS4*
替换 Py_UNICODE*
我们可以用 Py_UCS4
替换 Py_UNICODE
,并将这些 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 支持。
用 wchar_t*
替换 Py_UNICODE*
我们可以用 wchar_t
替换 Py_UNICODE
。由于 Py_UNICODE
已经是 wchar_t
的 typedef,因此这属于现状。
在 sizeof(wchar_t) == 4
的平台上,我们可以避免使用 UTF-8、UTF-16 和 UTF-32 编解码器从 wchar_t*
编码为字节对象时创建临时 Unicode 对象,就像“用 Py_UCS4*
替换 Py_UNICODE*
”这个想法一样。
优点
- 向后兼容。
- 在
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
,并且 u
在 PyUnicode_EncodeUTF8()
返回后可能会成为悬空引用。
讨论
- [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
最后修改时间: 2023-09-09 17:39:29 GMT