PEP 686 – 将 UTF-8 模式设为默认
- 作者:
- Inada Naoki <songofacandy at gmail.com>
- 讨论列表:
- Discourse 线程
- 状态:
- 已接受
- 类型:
- 标准跟踪
- 创建:
- 2022年3月18日
- Python 版本:
- 3.15
- 历史记录:
- 2022年3月18日, 2022年3月31日
- 决议:
- Discourse 消息
摘要
本 PEP 提案默认启用 UTF-8 模式。
通过此更改,Python 将始终使用 UTF-8 作为文件、标准输入/输出和管道的默认编码。
动机
UTF-8 成为事实上的标准文本编码。
- Python 源文件的默认编码为 UTF-8。
- JSON、TOML、YAML 使用 UTF-8。
- 大多数文本编辑器,包括 Visual Studio Code 和 Windows 记事本,都默认使用 UTF-8。
- 互联网上的大多数网站和文本数据都使用 UTF-8。
- 许多其他流行的编程语言,包括 Node.js、Go、Rust 和 Java,都默认使用 UTF-8。
将默认编码更改为 UTF-8 使 Python 更易于与它们互操作。
此外,许多使用 Unix 的 Python 开发人员忘记了默认编码是依赖于平台的。当他们读取使用 UTF-8 编码的文本文件(例如 JSON、TOML、Markdown 和 Python 源文件)时,他们会省略指定 encoding="utf-8"
。不一致的默认编码会导致许多错误。
规范
默认启用 UTF-8 模式
从 Python 3.15 开始,Python 将默认启用 UTF-8 模式。
用户仍然可以通过设置 PYTHONUTF8=0
或 -X utf8=0
来禁用 UTF-8 模式。
locale.getencoding()
由于 UTF-8 模式会影响 locale.getpreferredencoding(False)
,因此我们需要一个 API 来获取与 UTF-8 模式无关的区域设置编码。
locale.getencoding()
将为此目的而添加。它也返回区域设置编码,但会忽略 UTF-8 模式。
当指定 warn_default_encoding
选项时,locale.getpreferredencoding()
将发出 EncodingWarning
,就像 open()
一样(另请参见 PEP 597)。
此 API 已在 Python 3.11 中添加。
修复 encoding="locale"
选项
PEP 597 将 encoding="locale"
选项添加到 TextIOWrapper
中。此选项用于显式指定区域设置编码。TextIOWrapper
应在指定此选项时使用区域设置编码,而不管默认文本编码是什么。
但是,即使指定了 encoding="locale"
,TextIOWrapper
在 UTF-8 模式下也会使用 "UTF-8"
。此行为与 PEP 597 的动机不一致。这是因为我们没有预料到 Python 更改其默认文本编码时会将 UTF-8 模式设为默认。
在将 UTF-8 模式设为默认之前,应修复此不一致性。TextIOWrapper
即使在 UTF-8 模式下,也应在传递 encoding="locale"
时使用区域设置编码。
此问题已在 Python 3.11 中修复。
向后兼容性
大多数 Unix 系统使用 UTF-8 区域设置,当其区域设置为 C 或 POSIX 时,Python 会启用 UTF-8 模式。因此,此更改主要影响 Windows 用户。
当 Python 程序依赖于默认编码时,此更改可能会导致 UnicodeError
、乱码,甚至静默数据损坏。因此,应大声宣布此更改。
这是解决此向后兼容性问题的指南
- 禁用 UTF-8 模式。
- 使用
EncodingWarning
(PEP 597)查找 UTF-8 模式影响的每个位置。- 如果省略了
encoding
选项,请考虑使用encoding="utf-8"
或encoding="locale"
。 - 如果使用了
locale.getpreferredencoding()
,请考虑使用"utf-8"
或locale.getencoding()
。
- 如果省略了
- 在 UTF-8 模式下测试应用程序。
之前的示例
- Ruby 在 Ruby 3.0(2020 年)中 更改 了 Windows 上的默认
external_encoding
为 UTF-8。 - Java 在 JDK 18(2022 年)中 更改 了默认文本编码为 UTF-8。
Ruby 和 Java 都提供了一个用于向后兼容的选项。它们没有提供任何警告,例如 Python 中 PEP 597 的 EncodingWarning
,用于使用默认编码。
被拒绝的替代方案
弃用隐式编码
正在考虑弃用使用默认编码。
但是,在许多情况下,默认编码仅用于读取/写入 ASCII 文本。此外,此类警告对于在 Unix 上运行的非跨平台应用程序没有用。
因此,强制用户在任何地方都指定 encoding
非常痛苦。发出大量 DeprecationWarning
将导致用户忽略警告。
PEP 387 要求为向后不兼容的更改添加警告。但它不要求使用 DeprecationWarning
。因此,使用可选的 EncodingWarning
不会违反 PEP 387。
Java 也在 JEP 400 中拒绝了这个想法。
对管道使用 PYTHONIOENCODING
为了缓解向后兼容性问题,正在考虑在 subprocess
模块中使用 PYTHONIOENCODING
作为管道的默认编码。
有了这个想法,即使在 UTF-8 模式下,用户也可以对 subprocess.Popen(text=True)
使用旧版编码。
但此想法使“默认编码”变得复杂。而且此想法也与向后兼容性不符。
因此,此想法被拒绝。用户可以禁用 UTF-8 模式,直到他们用 encoding="utf-8"
或 encoding="locale"
替换 text=True
。
如何讲解
对于新用户,此更改减少了需要教授的内容。用户无需在第一年学习文本编码。他们应该在需要使用非 UTF-8 文本文件时学习它。
对于现有用户,请参阅 向后兼容性 部分。
版权
本文件置于公有领域或 CC0-1.0-Universal 许可证下,以较宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0686.rst