ZetCode

Python __bytes__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨了 Python 的 __bytes__ 方法,该特殊方法返回对象的字节表示。我们将介绍基本用法、序列化、二进制数据处理和实际示例。

基本定义

__bytes__ 方法返回一个表示对象值的 bytes 对象。它由内置的 bytes() 函数调用。

关键特性:它不接受任何参数(self 除外),必须返回一个 bytes 对象,并用于对象的序列化或二进制表示。与 __str__ 不同,它侧重于二进制数据而不是文本。

基本 __bytes__ 实现

这是一个简单的实现,展示了 __bytes__ 如何将对象转换为其二进制表示。这是更复杂示例的基础。

basic_bytes.py
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __bytes__(self):
        return bytes([self.x, self.y])

p = Point(3, 4)
print(bytes(p))  # b'\x03\x04'

此示例将 Point 对象转换为包含其坐标的 bytes 对象。__bytes__ 方法使用 bytes() 构造函数将 x 和 y 值打包到 bytes 对象中。

输出显示了以十六进制格式表示坐标 3 和 4 的两个字节。这是一个简单但有效的二进制表示。

序列化复杂对象

__bytes__ 可以通过将多个值组合成结构化的二进制格式来序列化更复杂的对象。这对于网络传输或文件存储很有用。

complex_serialization.py
import struct

class Person:
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height
    
    def __bytes__(self):
        name_bytes = self.name.encode('utf-8')
        return struct.pack(
            f'I{len(name_bytes)}sIf',
            len(name_bytes),
            name_bytes,
            self.age,
            self.height
        )

person = Person("Alice", 30, 1.75)
print(bytes(person))  # Binary representation

此示例使用 struct 模块创建 Person 对象的打包二进制表示。它通过包含长度前缀来处理可变长度字符串。

格式字符串 I{len(name_bytes)}sIf 指定:长度的无符号整数,名称的字节,年龄的无符号整数,以及身高的浮点数。这创建了一个定义明确的二进制结构。

网络协议实现

__bytes__ 对于实现需要转换为特定二进制格式进行传输的对象网络协议特别有用。

network_protocol.py
class NetworkPacket:
    def __init__(self, packet_type, sequence, payload):
        self.packet_type = packet_type
        self.sequence = sequence
        self.payload = payload
    
    def __bytes__(self):
        header = bytes([
            self.packet_type,
            (self.sequence >> 8) & 0xFF,
            self.sequence & 0xFF,
            len(self.payload)
        ])
        return header + self.payload

packet = NetworkPacket(1, 256, b'Hello')
print(bytes(packet))  # b'\x01\x01\x00\x05Hello'

此 NetworkPacket 类实现了简单的协议头以及有效负载数据。头包含类型、序列号(作为两个字节)和有效负载长度。

__bytes__ 方法通过打包头字段和连接有效负载来仔细构建二进制表示。此格式可以发送到网络连接上。

自定义二进制文件格式

__bytes__ 可用于创建知道如何将自身序列化为自定义二进制文件格式的对象,从而实现高效存储。

binary_file_format.py
class BMPHeader:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.file_size = 54 + (width * height * 3)
    
    def __bytes__(self):
        return (
            b'BM' +                     # Signature
            self.file_size.to_bytes(4, 'little') +
            bytes(4) +                  # Reserved
            (54).to_bytes(4, 'little') + # Pixel data offset
            (40).to_bytes(4, 'little') + # DIB header size
            self.width.to_bytes(4, 'little') +
            self.height.to_bytes(4, 'little') +
            (1).to_bytes(2, 'little') + # Planes
            (24).to_bytes(2, 'little') + # Bits per pixel
            bytes(24)                   # Padding
        )

header = BMPHeader(800, 600)
with open('image.bmp', 'wb') as f:
    f.write(bytes(header))

此示例创建了一个简化的 BMP 文件头。__bytes__ 方法通过组合具有特定字节顺序和填充的各种字段来构建二进制格式。

每个字段都转换为具有正确大小和字节序的字节。结果可以直接写入文件,创建有效的(尽管是空的)BMP 图像文件。

版本化序列化

更复杂的系统可以使用 __bytes__ 来实现版本化序列化,其中二进制格式根据版本要求而变化。

versioned_serialization.py
class Config:
    VERSION = 2
    
    def __init__(self, settings):
        self.settings = settings
    
    def __bytes__(self):
        if self.VERSION == 1:
            return self._v1_serialize()
        else:
            return self._v2_serialize()
    
    def _v1_serialize(self):
        return b'\x01' + b','.join(
            f"{k}={v}".encode('ascii') for k, v in self.settings.items()
        )
    
    def _v2_serialize(self):
        items = []
        for k, v in self.settings.items():
            key = k.encode('utf-8')
            val = str(v).encode('utf-8')
            items.append(len(key).to_bytes(2, 'big') + key +
                     len(val).to_bytes(2, 'big') + val)
        return b'\x02' + b''.join(items)

config = Config({'width': 1024, 'height': 768})
print(bytes(config))  # Version 2 binary format

此 Config 类实现了两个版本的二进制序列化。版本 1 使用简单的逗号分隔格式,而版本 2 使用带长度前缀的 UTF-8 字符串以获得更好的国际支持。

__bytes__ 方法充当调度程序,根据 VERSION 类属性选择适当的序列化方法。此模式允许格式演进,同时保持向后兼容性。

最佳实践

资料来源

作者

我叫 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。至今,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Python 教程