PEP 263 – 定义 Python 源代码编码
- 作者:
- Marc-André Lemburg <mal at lemburg.com>,Martin von Löwis <martin at v.loewis.de>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2001年6月6日
- Python 版本:
- 2.3
- 更新历史:
摘要
本 PEP 提出引入一种语法来声明 Python 源文件的编码。然后,Python 解析器使用给定的编码来解释该文件中的编码信息。最值得注意的是,这增强了对源代码中 Unicode 字面量的解释,并使得能够使用例如 UTF-8 直接在支持 Unicode 的编辑器中编写 Unicode 字面量。
问题
在 Python 2.1 中,Unicode 字面量只能使用基于 Latin-1 的编码“unicode-escape”来编写。这使得编程环境对于居住和工作在非 Latin-1 地区(例如许多亚洲国家)的 Python 用户来说不太友好。程序员可以使用他们喜欢的编码编写 8 位字符串,但对于 Unicode 字面量则必须使用“unicode-escape”编码。
提出的解决方案
我建议通过在文件顶部使用特殊的注释来声明编码,使 Python 源代码编码在每个源文件的基础上都可见且可更改。
为了使 Python 能够识别此编码声明,在处理 Python 源代码数据方面需要进行一些概念更改。
定义编码
如果未提供其他编码提示,Python 将默认使用 ASCII 作为标准编码。
要定义源代码编码,必须将魔术注释放置在源文件的第一行或第二行中,例如
# coding=<encoding name>
或(使用流行编辑器识别的格式)
#!/usr/bin/python
# -*- coding: <encoding name> -*-
或
#!/usr/bin/python
# vim: set fileencoding=<encoding name> :
更准确地说,第一行或第二行必须匹配以下正则表达式
^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)
然后将此表达式的第一组解释为编码名称。如果 Python 不认识该编码,则在编译期间会引发错误。包含编码声明的行上不能有任何 Python 语句。如果第一行匹配,则第二行将被忽略。
为了帮助像 Windows 这样的平台,这些平台会在 Unicode 文件的开头添加 Unicode BOM 标记,UTF-8 签名\xef\xbb\xbf
也将被解释为“utf-8”编码(即使没有提供魔术编码注释)。
如果源文件同时使用 UTF-8 BOM 标记签名和魔术编码注释,则注释中唯一允许的编码是“utf-8”。任何其他编码都会导致错误。
示例
以下是一些示例,以阐明在 Python 源文件顶部定义源代码编码的不同样式
- 使用解释器二进制文件和 Emacs 样式的文件编码注释
#!/usr/bin/python # -*- coding: latin-1 -*- import os, sys ... #!/usr/bin/python # -*- coding: iso-8859-15 -*- import os, sys ... #!/usr/bin/python # -*- coding: ascii -*- import os, sys ...
- 不使用解释器行,使用纯文本
# This Python file uses the following encoding: utf-8 import os, sys ...
- 文本编辑器可能具有定义文件编码的不同方式,例如
#!/usr/local/bin/python # coding: latin-1 import os, sys ...
- 如果没有编码注释,Python 的解析器将假定为 ASCII 文本
#!/usr/local/bin/python import os, sys ...
- 无法使用的编码注释
- 缺少“coding:”前缀
#!/usr/local/bin/python # latin-1 import os, sys ...
- 编码注释不在第 1 行或第 2 行
#!/usr/local/bin/python # # -*- coding: latin-1 -*- import os, sys ...
- 不支持的编码
#!/usr/local/bin/python # -*- coding: utf-42 -*- import os, sys ...
- 缺少“coding:”前缀
概念
PEP 基于以下概念,这些概念需要实现才能使用此类魔术注释
- 整个 Python 源文件应使用单个编码。不允许嵌入不同编码的数据,这将在编译 Python 源代码期间导致解码错误。
任何允许以上述方式处理前两行的编码都允许作为源代码编码,这包括 ASCII 兼容编码以及某些多字节编码(如 Shift_JIS)。它不包括所有字符都使用两个或多个字节的编码,例如 UTF-16。这样做的原因是为了使标记器中的编码检测算法保持简单。
- 转义序列的处理应该继续像现在一样工作,但使用所有可能的源代码编码,即标准字符串字面量(8 位和 Unicode)都受转义序列扩展的约束,而原始字符串字面量仅扩展非常小的转义序列子集。
- Python 的标记器/编译器组合需要更新为如下工作
- 读取文件
- 假设固定的每个文件编码将其解码为 Unicode
- 将其转换为 UTF-8 字节字符串
- 标记 UTF-8 内容
- 编译它,从给定的 Unicode 数据创建 Unicode 对象,并通过首先使用给定的文件编码将 UTF-8 数据重新编码为 8 位字符串数据来从 Unicode 字面量数据创建字符串对象
请注意,Python 标识符仅限于编码的 ASCII 子集,因此在步骤 4 之后不需要进一步转换。
实现
为了向后兼容当前在字符串字面量中使用非 ASCII 字符而未声明编码的现有代码,实现将分为两个阶段
- 允许在字符串字面量和注释中使用非 ASCII 字符,方法是在内部将缺少的编码声明视为“iso-8859-1”的声明。这将导致任意字节字符串在步骤 2 和步骤 5 的处理之间正确往返,并为包含非 ASCII 字节的 Unicode 字面量提供与 Python 2.2 的兼容性。
如果在输入中发现非 ASCII 字节,则会发出警告,每个编码不正确的输入文件发出一次警告。
- 删除警告,并将默认编码更改为“ascii”。
内置的compile()
API 将增强为接受 Unicode 作为输入。8 位字符串输入受上述编码检测的标准过程约束。
如果将带有编码声明的 Unicode 字符串传递给compile()
,则会引发SyntaxError
。
阶段
除了将默认编码更改为“ascii”之外,上述步骤 1 和 2 的实现已在 2.3 中完成。
在 2.5 版中,默认编码设置为“ascii”。
范围
本 PEP 旨在为从当前(或多或少)未定义的源代码编码情况升级到更健壮和可移植的定义提供升级路径。
参考文献
历史
- 1.10 及更高版本:请参阅 CVS 历史记录
- 1.8:在编码 RE 中添加了“.”。
- 1.7:在阶段 1 实现中添加了警告。用解释器的默认编码替换了 Latin-1 默认编码。对
compile()
进行了调整。 - 1.4 - 1.6:细微调整
- 1.3:Martin v. Loewis 参与了评论:UTF-8 BOM 标记检测、Emacs 样式魔术注释、实现的两阶段方法
版权
本文档已进入公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0263.rst