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
或 bytearrays
进行插值是有意义的,而弥补此缺失功能所需的工作会降低代码的整体可读性。
动机
随着 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
是因为字符串到字节的转换需要编码,而我们拒绝猜测;拒绝数字是因为
- 什么是数字是不确定的(浮点数?十进制数?分数?某些用户类型?)
- 允许数字会导致数字和数字的文本表示之间的歧义(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
的唯一目的是帮助从 Python 2 迁移,以及/或者拥有一个与 Python 2 共享的代码库。这很重要,因为在公开和私有环境中都存在一些模块,这些模块当前使用 Python 2 的 str
类型作为 bytes
容器,因此使用 %s
作为字节插值器。
但是,在新的、仅限 Python 3 的代码中应使用 %b
和 %a
,因此 %s
和 %r
将立即弃用,但不会从 3.x 系列中删除 [7]。
建议的变体
有人建议为 %b
的 str
参数自动使用 .encode('ascii','strict')
。
- 已拒绝,因为这会导致间歇性故障。最好让操作始终失败,以便能够正确修复问题所在。
有人建议当值为 str
时,让 %b
返回 ascii 编码的 repr(b’%b’ % ‘abc’ –> b“‘abc’”)。
- 已拒绝,因为这会导致难以调试的故障,且故障点远离问题所在。最好让操作始终失败,以便能够轻松修复问题所在。
最初,本 PEP 还建议添加格式样式格式化,但后来决定格式及其相关机制都严格基于文本(即 str
),因此将其删除。
提出了各种新的特殊方法,例如 __ascii__
、__format_bytes__
等;目前不需要这些方法,但如果实际使用表明此解决方案存在缺陷,则以后可以再次考虑。
另一个竞争 PEP,PEP 460 添加二进制插值和格式化,也存在。
异议
针对本 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
上次修改时间:2023-09-09 17:39:29 GMT