PEP 461 – 为 bytes 和 bytearray 添加 % 格式化
- 作者:
- Ethan Furman <ethan at stoneleaf.us>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2014年1月13日
- Python 版本:
- 3.5
- 发布历史:
- 2014年1月14日,2014年1月15日,2014年1月17日,2014年2月22日,2014年3月25日,2014年3月27日
- 决议:
- Python-Dev 消息
摘要
本 PEP 提议为 bytes 和 bytearray 添加类似于 Python 2 的 str 类型的 % 格式化操作 [1] [2]。
基本原理
虽然插值通常被认为是字符串操作,但在 bytes 或 bytearray 上进行插值是有意义的,而且弥补这种缺失功能所需的工作会损害代码的整体可读性。
动机
随着 Python 3 中 str 和 bytes 的分离,一个虽小但重要的编程领域变得略微困难,也更加痛苦——线缆格式协议 [3]。
这个编程领域的特点是二进制数据和 ASCII 兼容文本片段(又称 ASCII 编码文本)的混合。恢复对 bytes 和 bytearray 的受限 %-插值将有助于编写新的线缆格式代码,并有助于移植 Python 2 的线缆格式代码。
常见的用例包括 dbf 和 pdf 文件格式、email 格式以及 FTP 和 HTTP 通信等。
bytes 和 bytearray 格式化的提议语义
%-插值
所有数字格式代码(d、i、o、u、x、X、e、E、f、F、g、G 以及后续添加到 Python 3 的任何代码)都将得到支持,并且与 str 的工作方式相同,包括填充、对齐和其他相关修饰符(目前是 #、0、-、空格和 +(以及任何添加到 Python 3 的修饰符))。唯一允许的非数字代码是 c、b、a 和 s(它是 b 的同义词)。
对于数字代码,str 和 bytes(或 bytearray)插值之间的唯一区别是,这些代码的结果将是 ASCII 编码文本,而不是 unicode。换句话说,对于任何数字格式代码 %x
b"%x" % val
等价于
("%x" % val).encode("ascii")
示例
>>> b'%4x' % 10
b' a'
>>> b'%#4x' % 10
' 0xa'
>>> b'%04X' % 10
'000A'
%c 将插入一个字节,要么来自 range(256) 中的 int,要么来自长度为 1 的 bytes 参数,而不是来自 str。
示例
>>> b'%c' % 48
b'0'
>>> b'%c' % b'a'
b'a'
%b 将插入一系列字节。这些字节通过以下两种方式之一收集:
特别是,%b 不会接受数字或 str。str 被拒绝,因为字符串到字节的转换需要编码,我们拒绝猜测;数字被拒绝是因为
- 是什么构成数字是模糊的(浮点数?Decimal?Fraction?某些用户类型?)
- 允许数字会导致数字和数字的文本表示(3.14 与 '3.14')之间产生歧义
- 鉴于线缆格式的性质,显式肯定优于隐式
%s 作为 %b 的同义词被包含进来,唯一目的是使 2/3 代码库更易于维护。仅限 Python 3 的代码应使用 %b。
示例
>>> b'%b' % b'abc'
b'abc'
>>> b'%b' % 'some string'.encode('utf8')
b'some string'
>>> b'%b' % 3.14
Traceback (most recent call last):
...
TypeError: b'%b' does not accept 'float'
>>> b'%b' % 'hello world!'
Traceback (most recent call last):
...
TypeError: b'%b' does not accept 'str'
%a 将在插值值上给出 repr(some_obj).encode('ascii', 'backslashreplace') 的等效值。用例包括开发新协议并将标记写入流;调试进入现有协议的数据以查看问题是协议本身还是错误数据;序列化格式的回退;或者任何不适合定义 __bytes__ 但需要可读/信息性表示的情况 [6]。
%r 作为 %a 的同义词被包含进来,唯一目的是使 2/3 代码库更易于维护。仅限 Python 3 的代码使用 %a [7]。
示例
>>> b'%a' % 3.14
b'3.14'
>>> b'%a' % b'abc'
b"b'abc'"
>>> b'%a' % 'def'
b"'def'"
与 Python 2 的兼容性
如上所述,%s 和 %r 的 inclusion 仅仅是为了帮助从 Python 2 迁移,和/或拥有一个与 Python 2 兼容的单一代码库。这一点很重要,因为目前在野外和内部都有一些模块使用 Python 2 的 str 类型作为 bytes 容器,因此使用 %s 作为字节插值器。
然而,在新的、仅限 Python 3 的代码中应使用 %b 和 %a,因此 %s 和 %r 将立即被弃用,但不会从 3.x 系列中移除 [7]。
提议的变体
有人提议对 %b 的 str 参数自动使用 .encode('ascii','strict')。
- 被拒绝,因为这会导致间歇性故障。最好是操作总是失败,这样可以正确修复问题点。
有人提议让 %b 在值为 str 时返回 ascii 编码的 repr (b'%b' % 'abc' --> b"'abc'")。
- 被拒绝,因为这会导致远离问题点的难以调试的故障。最好是操作总是失败,这样可以轻松修复问题点。
最初这个 PEP 也提议添加格式风格的格式化,但后来决定 format 及其相关机制都严格基于文本(又称 str),因此被放弃了。
有人提议了各种新的特殊方法,例如 __ascii__、__format_bytes__ 等;目前不需要此类方法,但如果实际使用表明此解决方案存在缺陷,可以稍后再次讨论。
一个竞争的 PEP,PEP 460 Add binary interpolation and formatting 也存在。
反对意见
对这个 PEP 提出的反对意见主要围绕两个主题:
bytes和bytearray类型用于纯二进制数据,不假定任何编码- 提供假定 ASCII 编码的 %-插值将是一个诱人的麻烦,并让我们回到 Python 2 的
str/unicode文本模型的问题
正如讨论中所见,bytes 和 bytearray 也用于混合二进制数据和 ASCII 兼容片段:dbf 和 pdf 等文件格式,ftp 和 email 等网络协议。
bytes 和 bytearray 已经有几个方法假定 ASCII 兼容编码。例如 upper()、isalpha() 和 expandtabs() 等。%-插值及其非常受限的迷你语言,不会比现有方法更麻烦。
有些人反对允许所有数字格式代码,声称仅十进制就足够了。然而,至少有两种格式(dbf 和 pdf)使用非十进制数字。
脚注
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0461.rst