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