PEP 358 – “bytes” 对象
- 作者:
- Neil Schemenauer <nas at arctrix.com>,Guido van Rossum <guido at python.org>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2006年2月15日
- Python 版本:
- 2.6, 3.0
- 历史记录:
更新
此 PEP 部分已被 PEP 3137 取代。
摘要
此 PEP 概述了引入原始字节序列类型。添加 bytes 类型是向基于 Unicode 的 str 对象过渡的一步,该对象将在 Python 3.0 中引入。
PEP 描述了 bytes 类型在 Python 2.6 中的工作方式,以及它在 Python 3.0 中的工作方式。(偶尔会有差异,因为在 Python 2.6 中,我们有两种字符串类型,str 和 unicode,而在 Python 3.0 中,我们只有一种字符串类型,其名称为 str,但其语义类似于 2.6 的 unicode 类型。)
动机
Python 当前的字符串对象是重载的。它们用于保存字符序列和字节序列。这种目的的重载会导致混淆和错误。在未来的 Python 版本中,字符串对象将用于保存字符数据。bytes 对象将充当字节容器的角色。最终,unicode 类型将重命名为 str,旧的 str 类型将被删除。
规范
bytes 对象存储一个可变的整数序列,这些整数的范围是 0 到 255。与字符串对象不同,索引 bytes 对象会返回一个整数。将不是整数的对象分配或比较到元素会导致 TypeError
异常。将元素分配到 0 到 255 范围之外的值会导致 ValueError
异常。.__len__()
方法返回序列中存储的整数数量(即字节数)。
bytes 对象的构造函数具有以下签名
bytes([initializer[, encoding]])
如果未提供任何参数,则创建一个包含零个元素的 bytes 对象并返回。初始化参数可以是字符串(在 2.6 中,str 或 unicode),整数的可迭代对象或单个整数。构造函数的伪代码(针对清晰的语义进行了优化,而不是针对速度)如下所示:
def bytes(initializer=0, encoding=None):
if isinstance(initializer, int): # In 2.6, int -> (int, long)
initializer = [0]*initializer
elif isinstance(initializer, basestring):
if isinstance(initializer, unicode): # In 3.0, "if True"
if encoding is None:
# In 3.0, raise TypeError("explicit encoding required")
encoding = sys.getdefaultencoding()
initializer = initializer.encode(encoding)
initializer = [ord(c) for c in initializer]
else:
if encoding is not None:
raise TypeError("no encoding allowed for this initializer")
tmp = []
for c in initializer:
if not isinstance(c, int):
raise TypeError("initializer must be iterable of ints")
if not 0 <= c < 256:
raise ValueError("initializer element out of range")
tmp.append(c)
initializer = tmp
new = <new bytes object of length len(initializer)>
for i, c in enumerate(initializer):
new[i] = c
return new
.__repr__()
方法返回一个字符串,该字符串可以计算以生成一个新的 bytes 对象,其中包含一个字节文字
>>> bytes([10, 20, 30])
b'\n\x14\x1e'
该对象具有一个 .decode()
方法,相当于 str 对象的 .decode()
方法。该对象有一个类方法 .fromhex()
,它接收来自集合 [0-9a-fA-F ]
的字符的字符串并返回一个 bytes 对象(类似于 binascii.unhexlify
)。例如
>>> bytes.fromhex('5c5350ff')
b'\\SP\xff'
>>> bytes.fromhex('5c 53 50 ff')
b'\\SP\xff'
该对象具有一个 .hex()
方法,它执行反向转换(类似于 binascii.hexlify
)
>> bytes([92, 83, 80, 255]).hex()
'5c5350ff'
bytes 对象有一些类似于列表方法的方法,另一些类似于 str 方法。以下是完整的方法列表及其近似签名
.__add__(bytes) -> bytes
.__contains__(int | bytes) -> bool
.__delitem__(int | slice) -> None
.__delslice__(int, int) -> None
.__eq__(bytes) -> bool
.__ge__(bytes) -> bool
.__getitem__(int | slice) -> int | bytes
.__getslice__(int, int) -> bytes
.__gt__(bytes) -> bool
.__iadd__(bytes) -> bytes
.__imul__(int) -> bytes
.__iter__() -> iterator
.__le__(bytes) -> bool
.__len__() -> int
.__lt__(bytes) -> bool
.__mul__(int) -> bytes
.__ne__(bytes) -> bool
.__reduce__(...) -> ...
.__reduce_ex__(...) -> ...
.__repr__() -> str
.__reversed__() -> bytes
.__rmul__(int) -> bytes
.__setitem__(int | slice, int | iterable[int]) -> None
.__setslice__(int, int, iterable[int]) -> Bote
.append(int) -> None
.count(int) -> int
.decode(str) -> str | unicode # in 3.0, only str
.endswith(bytes) -> bool
.extend(iterable[int]) -> None
.find(bytes) -> int
.index(bytes | int) -> int
.insert(int, int) -> None
.join(iterable[bytes]) -> bytes
.partition(bytes) -> (bytes, bytes, bytes)
.pop([int]) -> int
.remove(int) -> None
.replace(bytes, bytes) -> bytes
.rindex(bytes | int) -> int
.rpartition(bytes) -> (bytes, bytes, bytes)
.split(bytes) -> list[bytes]
.startswith(bytes) -> bool
.reverse() -> None
.rfind(bytes) -> int
.rindex(bytes | int) -> int
.rsplit(bytes) -> list[bytes]
.translate(bytes, [bytes]) -> bytes
请注意,明显缺少 .isupper()
、.upper()
及其相关方法。(但请参见下面的“未解决的问题”。)没有 .__hash__()
,因为该对象是可变的。没有使用 .sort()
方法的用例。
bytes 类型也支持缓冲区接口,支持读取和写入二进制(但不是字符)数据。
超出范围的问题
- Python 3k 将拥有一个截然不同的 I/O 子系统。决定该 I/O 子系统如何工作以及与 bytes 对象交互不在此 PEP 的范围内。但是,预期二进制 I/O 将读取和写入字节,而文本 I/O 将读取字符串。由于 bytes 类型支持缓冲区接口,因此 Python 2.6 中现有的二进制 I/O 操作将支持 bytes 对象。
- 有人建议向语言中添加一个名为
.__bytes__()
的特殊方法,以允许将对象转换为字节数组。此决定不在范围内。 - 还提出了形式为
b"..."
的字节文字。这是 PEP 3112 的主题。
未解决的问题
.decode()
方法是冗余的,因为 bytes 对象b
也可以通过调用unicode(b, <encoding>)
(在 2.6 中)或str(b, <encoding>)
(在 3.0 中)来解码。我们是否需要 encode/decode 方法?从某种意义上说,使用构造函数的拼写更简洁。- 需要更仔细地指定方法。
- 需要指定腌制和编组支持。
- 是否真的需要实现所有这些列表方法?
- 可以考虑支持
.ljust()
、.rjust()
、.center()
,并带有强制的第二个参数。 - 可以考虑支持
.split()
,并带有强制参数。 - 甚至可以考虑支持
.islower()
、.isupper()
、.isspace()
、.isalpha()
、.isalnum()
、.isdigit()
和相应的转换(.lower()
等),使用 ASCII 定义的字母、数字和空格。如果接受此建议,则.ljust()
、.rjust()
、.center()
和.split()
的情况会变得更加有力,并且它们也应该具有默认参数,使用 ASCII 空格或所有 ASCII 空格(对于.split()
)。
常见问题
问:当 Unicode 对象的 encode 方法执行相同操作时,为什么还要有可选的 encoding 参数?
答:在当前版本的 Python 中,encode 方法返回一个 str 对象,我们无法在不破坏代码的情况下更改它。构造 bytes(s.encode(...))
代价很高,因为它必须多次复制字节序列。此外,Python 通常提供两种将类型为 A 的对象转换为类型为 B 的对象的方法:要求 A 实例将自身转换为 B,或要求类型 B 从 A 创建一个新实例。根据 A 和 B 的具体情况,这两个 API 都有意义;有时出于解耦的原因,要求 A 不能了解 B,在这种情况下,您必须使用后一种方法;有时 B 不能了解 A,在这种情况下,您必须使用前一种方法。
问:如果初始化参数是 str,为什么 bytes 会忽略 encoding 参数?(这仅适用于 2.6。)
答:在这种情况下,encoding 没有合理的含义。str 对象是字节数组,并且它们不知道包含的字符数据的编码。我们需要假设程序员提供了已经使用所需编码的 str 对象。如果您需要纯字节副本以外的其他内容,则需要先解码字符串。例如
bytes(s.decode(encoding1), encoding2)
问:为什么不将 encoding 参数的默认值设置为 Latin-1(或涵盖整个字节范围的其他编码)而不是 ASCII?
答:Python 的系统默认编码是 ASCII。使用该默认值似乎最不容易造成混淆。此外,在 Py3k 中,使用 Latin-1 作为默认值可能不是用户期望的。例如,他们可能更喜欢 Unicode 编码。任何默认值都不会始终如一地按预期工作。至少 ASCII 会在您尝试编码非 ASCII 数据时发出响亮的抱怨。
版权
本文档已进入公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0358.rst