Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

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

最后修改:2025-02-01 08:59:27 GMT