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 异常。bytes 的 .__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 对象的字符串
>>> 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"..."形式的 bytes 字面量。这是 PEP 3112 的主题。
未解决的问题
.decode()方法是多余的,因为 bytes 对象b也可以通过调用unicode(b, <encoding>)(在 2.6 中)或str(b, <encoding>)(在 3.0 中)来解码。我们是否需要 encode/decode 方法?从某种意义上说,使用构造函数的拼写更简洁。- 需要更仔细地指定这些方法。
- 需要指定 Pickling 和 marshalling 支持。
- 真的应该实现所有这些列表方法吗?
- 可以提出支持
.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