PEP 223 – 更改 \x
转义符的含义
- 作者:
- Tim Peters <tim.peters at gmail.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2000-08-20
- Python 版本:
- 2.0
- 历史记录:
- 2000-08-23
摘要
更改 \x
转义符(在 8 位和 Unicode 字符串中)以精确地使用其后的两个十六进制数字。该提案将此视为纠正原始设计缺陷,从而在所有字符串类型中实现更清晰的表达,更清晰的 Unicode 故事,与 Perl 正则表达式更好的兼容性,并且对现有代码的风险最小。
语法
所有类型非原始字符串中 \x
转义符的语法变为
\xhh
其中 h 是十六进制数字(0-9,a-f,A-F)。1.5.2 中的精确语法在参考手册中没有明确说明;它说
\xhh...
这意味着“两个或多个”十六进制数字,但 1.5.2 编译器也接受一位数字形式,并且一个简单的 \x
被“扩展”为自身(即反斜杠后跟字母 x)。目前尚不清楚参考手册是否打算使用一位数字或零位数字行为。
语义
在 8 位非原始字符串中,
\xij
扩展为字符
chr(int(ij, 16))
请注意,这与 1.6 及之前版本相同。
在 Unicode 字符串中,
\xij
与以下相同
\u00ij
即它扩展为 Unicode 空间初始段中的明显拉丁-1 字符。
在 8 位字符串中,一个后面没有至少两个十六进制数字的 \x
是编译时错误,具体来说是 ValueError
,在 Unicode 字符串中是 UnicodeError
(ValueError
的子类)。请注意,如果 \x
后面跟着两个以上的十六进制数字,则只“使用”前两个。在 1.6 及之前版本中,除了最后两个以外,所有其他数字都被静默忽略。
示例
在 1.5.2 中
>>> "\x123465" # same as "\x65"
'e'
>>> "\x65"
'e'
>>> "\x1"
'\001'
>>> "\x\x"
'\\x\\x'
>>>
在 2.0 中
>>> "\x123465" # \x12 -> \022, "3456" left alone
'\0223456'
>>> "\x65"
'e'
>>> "\x1"
[ValueError is raised]
>>> "\x\x"
[ValueError is raised]
>>>
历史和理由
\x
转义符是在 C 中引入的一种方法,用于指定可变宽度字符编码。这些编码究竟是什么,以及它们需要多少个十六进制数字,由每个实现决定。该语言只是简单地说明 \x
“使用”所有后面的十六进制数字,并将含义留给每个实现决定。因此,实际上,C 中的 \x
是一个标准钩子,用于提供平台定义的行为。
由于 Python 明确地以平台无关性为目标,因此 Python 中的 \x
转义符(直至 1.6 版本)在所有平台上都被视为相同:除了最后两个十六进制数字被静默忽略。因此,Python 中 \x
转义符的唯一实际用途是使用十六进制表示法指定单个字节。
Larry Wall 似乎意识到这是平台无关语言中 \x
转义符的唯一真正用途,因为 Python 2.0 的提议规则实际上是 Perl 从一开始就采用的方法(尽管你需要在 Perl -w 模式下运行才能获得有关后面不到 2 个十六进制数字的 \x
转义符的警告 - 始终坚持 2 个明显更 Pythonic 的方法)。
当 Unicode 字符串被引入到 Python 时,\x
被泛化,以便在 Unicode 字符串中忽略除最后四个十六进制数字以外的所有数字。这给新的正则表达式引擎带来了技术困难:SRE 非常努力地以直观的方式允许混合 8 位和 Unicode 模式和字符串,它不再有任何方法来猜测例如 r"\x123456"
作为模式的含义:它是在要求匹配 8 位字符 \x56
还是 Unicode 字符 \u3456
?
有一些 hacky 的方法可以猜测,但这并没有结束。ISO C99 标准还引入了 8 位数字 \U12345678
转义符来覆盖整个 ISO 10646 字符空间,并且 Python 2 也希望从一开始就支持这一点。那么 \x
转义符应该意味着什么?它们会忽略除最后八个十六进制数字以外的所有数字吗?如果在 Unicode 字符串中少于 8 个,则会忽略除最后 4 个以外的所有数字?如果少于 4 个,则会忽略除最后 2 个以外的所有数字?
这种情况每分钟都变得更加混乱,该提议通过使 \x
更简单而不是更复杂来解开这个戈尔迪之结。请注意,对 Unicode 字符串中 \xijkl
的 4 位数字泛化也是多余的,因为它与 Unicode 字符串中的 \uijkl
具有完全相同的含义。用一种显而易见的方法来通过十六进制表示法指定 Unicode 字符,这更符合 Pythonic 的风格。
开发和讨论
该提议是在 Guido van Rossum、Fredrik Lundh 和 Tim Peters 之间通过电子邮件协商完成的。随后在 Python-Dev 上以“Go x yourself”为主题进行了解释和讨论 [1],从 2000-08-03 开始。响应极其正面;没有提出异议。
向后兼容性
更改 \x
转义符的含义确实存在破坏现有代码的风险,尽管尚未发现任何不兼容的实例。据信该风险极小。
Tim Peters 验证了,除了故意激发最终情况的标准测试套件的一部分外,在 Python CVS 开发树中或他机器上各种 Python 包中,都没有 \xabcdef...
的实例,这些实例后面跟着少于或多于 2 个十六进制数字。
不太可能出现少于 2 个的情况,因为参考手册暗示它们是非法的(尽管这是可以争论的!)。如果有任何实例后面跟着超过 2 个,Guido 准备争辩说无论如何它们都是有错误的 <0.9 wink>。
Guido 报告说,O'Reilly 的 Python 书籍已经记录了 Python 以提议的方式工作,这可能是由于它们的 Perl 编辑传统(如上所述,Perl 从一开始就以(非常接近)提议的方式工作)。
Finn Bock 报告说,JPython 今天对 \x
转义符的处理是不可预测的。该提议给出了一个清晰的含义,可以在所有 Python 实现中一致且轻松地实现。
对其他工具的影响
据信没有。可能出现损坏的候选者主要是解析工具,但作者不知道任何对 Python 字符串的内部结构感到担心的工具,除了“当出现反斜杠时,吞掉下一个字符”这一近似值。Tim Peters 检查了 python-mode.el
、标准 tokenize.py
和 pyclbr.py
以及 IDLE 语法着色子系统,并且相信不需要更改任何一个。像 tabnanny.py
和 checkappend.py
这样的工具从 tokenize.py
继承了它们的免疫力。
参考实现
代码更改非常简单,因此不会产生单独的补丁。Fredrik Lundh 正在编写代码,他是该领域的专家,他将在 2.0b1 发布之前简单地检查这些更改。
BDFL 声明
是的,ValueError
,而不是 SyntaxError
。“字面解释问题通常会引发‘运行时’异常,而不是语法错误。”
参考资料
版权
本文件已置于公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0223.rst