PEP 467 – 二进制序列的微小 API 改进
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>, Ethan Furman <ethan at stoneleaf.us>
- 讨论对象:
- Discourse 讨论帖
- 状态:
- 草稿
- 类型:
- 标准跟踪
- 创建日期:
- 2014-03-30
- Python 版本:
- 3.13
- 历史记录:
- 2014-03-30, 2014-08-15, 2014-08-16, 2016-06-07, 2016-09-01, 2021-04-13, 2021-11-03, 2023-12-27
摘要
本 PEP 提出对 bytes
和 bytearray
类型的 API 进行少量调整,使其更易于完全在二进制域中操作
- 添加
fromsize
替代构造函数 - 添加
fromint
替代构造函数 - 添加
getbyte
字节检索方法 - 添加
iterbytes
替代迭代器
基本原理
在 Python 3 语言规范的最初开发过程中,用于任意二进制数据的核心 bytes
类型最初是现在称为 bytearray
的可变类型。Python 3 系列中二进制域操作的其他方面也随着时间的推移而发展,例如,PEP 461。
动机
随着 Python 3 以及 str
和 bytes
之间的划分,一个小的但重要的编程领域变得稍微困难,而且更加痛苦——线格式协议。
这个编程领域以二进制数据和 ASCII 兼容的文本片段(也称为 ASCII 编码的文本)混合为特征。添加新的构造函数、方法和迭代器将有助于编写新的线格式代码,以及移植任何剩余的 Python 2 线格式代码。
常见的用例包括 dbf
和 pdf
文件格式、email
格式以及 FTP
和 HTTP
通信,等等。
提案
添加显式的“计数和字节初始化序列”构造函数
为了替换从基本构造函数创建零填充 bytes
类对象的做法(不推荐,例如 bytes(1)
-> b'\x00'
),本 PEP 提出添加一个显式的 fromsize
替代构造函数,作为 bytes
和 bytearray
上的类方法,其第一个参数是计数,第二个参数是要使用的填充字节(默认为 \x00
)。
>>> bytes.fromsize(3)
b'\x00\x00\x00'
>>> bytearray.fromsize(3)
bytearray(b'\x00\x00\x00')
>>> bytes.fromsize(5, b'\x0a')
b'\x0a\x0a\x0a\x0a\x0a'
>>> bytearray.fromsize(5, fill=b'\x0a')
bytearray(b'\x0a\x0a\x0a\x0a\x0a')
fromsize
的行为将与当前构造函数在传递单个整数时的行为相同,同时允许在需要时使用非零填充值。
添加显式的“单个字节”构造函数
作为文本 chr
函数的二进制对应物,本 PEP 提出添加一个显式的 fromint
替代构造函数,作为 bytes
和 bytearray
上的类方法。
>>> bytes.fromint(65)
b'A'
>>> bytearray.fromint(65)
bytearray(b'A')
这些方法只接受 0 到 255(含)范围内的整数。
>>> bytes.fromint(512)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: integer must be in range(0, 256)
>>> bytes.fromint(1.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
ord
内置函数的文档将更新,明确指出 bytes.fromint
是二进制数据的首要逆操作,而 chr
是文本数据的逆操作,以及 bytearray.fromint
也存在。
从行为上讲,bytes.fromint(x)
等效于当前的 bytes([x])
(bytearray
也是如此)。新的拼写预计更容易发现,更容易阅读(尤其是在与二进制序列类型上的索引操作结合使用时)。
作为单独的方法,新的拼写也将与更高阶函数(如 map
)更好地配合使用。
这些新方法有意不提供与现有 int.to_bytes
转换方法相同的通用整数支持级别,该方法允许将任意大的整数转换为任意长的字节对象。将接受的整数范围限制为单个字节内正整数,这意味着不需要字节顺序信息,也不需要处理负数。新方法的文档会将读者引导到 int.to_bytes
,用于需要处理任意整数的用例。
添加“getbyte”方法以检索单个字节
本 PEP 提出 bytes
和 bytearray
获取 getbyte
方法,该方法始终返回 bytes
。
>>> b'abc'.getbyte(0)
b'a'
如果请求的索引不存在,则会引发 IndexError
。
>>> b'abc'.getbyte(9)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: index out of range
添加生成 bytes
对象的优化迭代器方法
本 PEP 提出 bytes
和 bytearray
获取一个优化过的 iterbytes
方法,该方法生成长度为 1 的 bytes
对象而不是整数。
for x in data.iterbytes():
# x is a length 1 ``bytes`` object, rather than an integer
例如
>>> tuple(b"ABC".iterbytes())
(b'A', b'B', b'C')
设计讨论
为什么不依赖序列重复来创建零初始化的序列?
可以通过序列重复创建零初始化的序列。
>>> b'\x00' * 3
b'\x00\x00\x00'
>>> bytearray(b'\x00') * 3
bytearray(b'\x00\x00\x00')
然而,这在 bytearray
类型最初设计时也是如此,并且决定在类型构造函数中添加对它的显式支持。当 bytes
类型在 PEP 3137 中引入时,它继承了该特性。
本 PEP 并未重新审视最初的设计决定,只是更改了拼写,因为用户有时会发现二进制序列构造函数的当前行为令人惊讶。尤其是,有一种合理的理由可以说明 bytes(x)
(其中 x
是一个整数)应该像本 PEP 中提出的 bytes.fromint(x)
一样。通过将两种行为作为单独的类方法提供,可以避免这种歧义。
当前的解决方法
经过近十年的时间,似乎还没有关于字节迭代最佳解决方法的共识,正如 Get single-byte bytes objects from bytes objects 所示。
省略最初提出的内置函数
当提交给指导委员会时,本 PEP 提出引入一个 bchr
内置函数(与 bytes.fromint
的行为相同),在不同的命名方案下重新创建 Python 2 中的 ord
/chr
/unichr
三元组(ord
/bchr
/chr
)。
指导委员会表示,他们认为这种功能的使用频率不足以证明提供两种执行相同操作的方法的必要性,尤其是当其中一种方法是新的内置函数时。因此,该提案的这一部分被放弃,因为它与 bytes.fromint
替代构造函数冗余。
频繁使用这种方法的开发者可以选择定义自己的 bchr = bytes.fromint
别名。
范围限制:内存视图
使用新项检索方法更新 memoryview
并不在本 PEP 的范围之内。
参考资料
版权
本文件已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0467.rst
最后修改日期:2024-02-07 13:44:54 GMT