2025-10-08 Python 标准库 7——内置类型:二进制序列
文章目录
- 1. bytes 对象
- 1.1. bytes 的创建方式
- 1.1.1. 字面值创建(b 前缀)
- 1.1.2. 构造器创建(`bytes()`)
- 1.1.3. 十六进制字符串转换(`bytes.fromhex()`)
- 1.2. bytes 的核心特性与方法
- 1.2.1. 序列特性(索引与切片)
- 1.2.2. 十六进制转换(`bytes.hex()`)
- 1.2.3. 解码为字符串(`bytes.decode()`)
- 2. bytearray 对象
- 2.1. bytearray 的创建方式
- 2.1.1. 构造器创建(`bytearray()`)
- 2.1.2. 十六进制字符串转换(`bytearray.fromhex()`)
- 2.2. bytearray 的可变特性
- 2.3. bytearray 的关键方法
- 3. bytes 与 bytearray 的共同操作
- 3.1. 通用序列方法
- 3.2. 基于 ASCII 的方法
- 3.3. printf 风格的二进制格式化
- 4. memoryview 对象
- 4.1. memoryview 的创建与基础特性
- 4.1.1. 创建方式(`memoryview(obj)`)
- 4.1.2. 序列特性(索引与切片)
- 4.2. memoryview 的核心方法
- 4.2.1. 数据转换(`tolist()`、`tobytes()`)
- 4.2.2. 格式与形状转换(`cast()`)
- 4.2.3. 内存释放(`release()` 与上下文管理)
- 4.3. memoryview 关键属性
- 5. 总结
参考文档:内置类型 — Python 3.13.7 文档
1. bytes 对象
bytes
是 Python 中处理二进制数据的不可变序列类型,每个元素是 0~255 范围内的整数(对应单个字节)。由于许多二进制协议基于 ASCII 设计,bytes
提供了部分与字符串(str
)相似的方法,但仅适用于 ASCII 兼容场景;对于任意二进制数据,需避免盲目使用文本处理逻辑(防止数据损坏)。
1.1. bytes 的创建方式
bytes
对象可通过字面值、构造器或专用类方法创建,以下是常见方式及代码示例:
1.1.1. 字面值创建(b 前缀)
语法与字符串类似,但需添加 b
前缀,仅支持 ASCII 字符(非 ASCII 需用转义序列,如 \xXX
)。支持单引号、双引号、三重引号,也可通过 r
前缀禁用转义。
# 单引号/双引号(支持嵌套)
b1 = b'hello "world"'
b2 = b'he said \'hi\''
print(b1) # 输出:b'hello "world"'
print(b2) # 输出:b'he said \'hi\''# 三重引号(多行二进制数据)
b3 = b'''line1
line2'''
print(b3) # 输出:b'line1\nline2'(保留换行符)# 非 ASCII 需转义(\xXX 表示十六进制字节)
b4 = b'\xe4\xb8\xad\xe6\x96\x87' # UTF-8 编码的“中文”
print(b4.decode('utf-8')) # 输出:中文(解码为字符串)# r 前缀禁用转义
b5 = rb'C:\Users\xxx' # \x 不被当作转义
print(b5) # 输出:b'C:\\Users\\xxx'
1.1.2. 构造器创建(bytes()
)
构造器支持多种参数形式,灵活生成 bytes
对象:
bytes(n)
:创建长度为n
、以 0 填充的bytes
;bytes(iterable)
:通过 0~255 的整数可迭代对象创建;bytes(obj)
:通过支持缓冲区协议的对象(如bytearray
、memoryview
)复制创建。
# 1. 长度为 5、0 填充的 bytes
b1 = bytes(5)
print(b1) # 输出:b'\x00\x00\x00\x00\x00'# 2. 通过整数可迭代对象(range(20) 需在 0~255 内)
b2 = bytes(range(5)) # 整数 0~4 对应字节 \x00~\x04
print(b2) # 输出:b'\x00\x01\x02\x03\x04'# 3. 复制 bytearray 对象(缓冲区协议)
ba = bytearray(b'hello')
b3 = bytes(ba)
print(b3) # 输出:b'hello'(与 ba 内容相同,但不可变)
1.1.3. 十六进制字符串转换(bytes.fromhex()
)
类方法 fromhex(string)
将十六进制字符串解码为 bytes
,忽略字符串中的 ASCII 空白符(空格、制表符等)。
版本变更:Python 3.7+ 支持忽略所有 ASCII 空白符(此前仅忽略空格)。
# 基础用法:十六进制字符串转 bytes
b1 = bytes.fromhex('2E f0 F1 f2') # 忽略空格,2E 对应 .,f0~f2 对应 \xf0~\xf2
print(b1) # 输出:b'.\xf0\xf1\xf2'# 忽略多种空白符(3.7+ 支持)
b2 = bytes.fromhex('a1\tb2\nc3') # 忽略制表符、换行符
print(b2) # 输出:b'\xa1\xb2\xc3'
1.2. bytes 的核心特性与方法
1.2.1. 序列特性(索引与切片)
bytes
是序列类型,但索引返回整数(对应字节的数值,0~255),切片返回长度为 1 的 bytes
对象(与 str
索引返回字符不同)。
b = b'abc'
# 索引:返回整数(a 的 ASCII 码是 97)
print(b[0]) # 输出:97
# 切片:返回 bytes 对象
print(b[0:1]) # 输出:b'a'
# 反向索引
print(b[-1]) # 输出:99(c 的 ASCII 码)
1.2.2. 十六进制转换(bytes.hex()
)
实例方法 hex(sep=None, bytes_per_sep=1)
将 bytes
转换为十六进制字符串,可选参数 sep
用于添加分隔符,bytes_per_sep
控制分隔符位置(正值从右数,负值从左数)。
版本变更:Python 3.8+ 支持 sep
和 bytes_per_sep
参数。
b = b'\xf0\xf1\xf2\xf3'
# 基础用法:无分隔符
print(b.hex()) # 输出:'f0f1f2f3'# 添加分隔符(-)
print(b.hex('-')) # 输出:'f0-f1-f2-f3'# 控制分隔符位置(每 2 个字节加 _)
print(b.hex('_', 2)) # 输出:'f0f1_f2f3'# 从左数每 3 个字节加空格(负值)
b2 = b'555544444c52'
print(b2.hex(' ', -4)) # 输出:'5555 4444 4c52'
1.2.3. 解码为字符串(bytes.decode()
)
将 bytes
按指定编码(默认 utf-8
)解码为 str
,errors
参数控制编码错误处理(如 strict
抛错、replace
替换为 �
)。
版本变更:Python 3.9+ 在开发/调试模式下检查 errors
参数有效性。
b = b'\xe4\xb8\xad\xe6\x96\x87' # UTF-8 编码的“中文”
# 正常解码
print(b.decode('utf-8')) # 输出:中文# 编码不匹配时处理错误
b_error = b'\xe4\xb8\xad\xe6' # 不完整的 UTF-8 字节
print(b_error.decode('utf-8', errors='replace')) # 输出:中�(替换错误字节)
2. bytearray 对象
bytearray
是 bytes
的可变版本,支持修改元素值,但元素仍需是 0~255 的整数。bytearray
无专属字面值,需通过构造器创建。
2.1. bytearray 的创建方式
2.1.1. 构造器创建(bytearray()
)
构造器参数与 bytes
类似,但返回可变对象:
bytearray()
:空bytearray
;bytearray(n)
:长度为n
、0 填充的bytearray
;bytearray(iterable)
:通过 0~255 的整数可迭代对象创建;bytearray(obj)
:通过缓冲区协议对象复制创建(如bytes
、str
,str
需指定编码,或通过bytearray(b'xxx')
)。
# 1. 空 bytearray
ba1 = bytearray()
print(ba1) # 输出:bytearray(b'')# 2. 长度为 3、0 填充
ba2 = bytearray(3)
print(ba2) # 输出:bytearray(b'\x00\x00\x00')# 3. 整数可迭代对象
ba3 = bytearray(range(97, 100)) # 97~99 对应 a~c
print(ba3) # 输出:bytearray(b'abc')# 4. 复制 bytes 对象
b = b'hello'
ba4 = bytearray(b)
print(ba4) # 输出:bytearray(b'hello')
2.1.2. 十六进制字符串转换(bytearray.fromhex()
)
与 bytes.fromhex()
功能一致,将十六进制字符串解码为 bytearray
,忽略 ASCII 空白符。
版本变更:Python 3.7+ 支持忽略所有 ASCII 空白符。
ba = bytearray.fromhex('a1 b2 c3')
print(ba) # 输出:bytearray(b'\xa1\xb2\xc3')
2.2. bytearray 的可变特性
bytearray
支持修改单个元素或切片(元素需为 0~255 的整数,切片需为 bytes
或 bytearray
),但无法改变长度(需通过 append
、extend
等方法调整长度)。
ba = bytearray(b'abc')
# 1. 修改单个元素(97 是 a,改为 65(A))
ba[0] = 65
print(ba) # 输出:bytearray(b'Abc')# 2. 修改切片(替换为 bytes)
ba[1:3] = b'XY'
print(ba) # 输出:bytearray(b'AXY')# 3. 调整长度(append 单个字节,extend 多个字节)
ba.append(90) # 添加 Z(90 是 Z 的 ASCII 码)
ba.extend(b'12') # 扩展 b'12'
print(ba) # 输出:bytearray(b'AXYZ12')# 错误:元素超出 0~255
# ba[0] = 300 # 报错:ValueError: byte must be in range(0, 256)
2.3. bytearray 的关键方法
bytearray
继承了 bytes
的大部分方法,但需注意:多数方法并非原地操作,而是返回新对象(如 replace
、strip
),仅 append
、extend
、pop
等是原地操作。
ba = bytearray(b'hello world')
# 1. replace(返回新 bytearray,原对象不变)
new_ba = ba.replace(b'world', b'python')
print(ba) # 输出:bytearray(b'hello world')(原对象不变)
print(new_ba) # 输出:bytearray(b'hello python')(新对象)# 2. strip(移除首尾空白,返回新对象)
ba2 = bytearray(b' test ')
stripped_ba = ba2.strip()
print(stripped_ba) # 输出:bytearray(b'test')# 3. 原地操作(pop 移除最后一个元素)
ba3 = bytearray(b'abc')
ba3.pop()
print(ba3) # 输出:bytearray(b'ab')
3. bytes 与 bytearray 的共同操作
bytes
和 bytearray
均支持通用序列操作,部分方法还支持 bytes-like object
(如 bytes
、bytearray
、memoryview
)作为参数;此外,二者还提供基于 ASCII 的文本操作(需注意仅适用于 ASCII 数据)。
3.1. 通用序列方法
以下方法适用于任意二进制数据,不依赖 ASCII 格式:
方法 | 功能描述 |
---|---|
count(sub[, start[, end]]) | 统计 sub 在 [start, end] 内非重叠出现次数,sub 可为 bytes-like 或 0~255 整数 |
find(sub[, start[, end]]) | 查找 sub 的最小索引,未找到返回 -1,sub 支持 bytes-like 或整数 |
index(sub[, start[, end]]) | 同 find ,但未找到抛 ValueError |
join(iterable) | 拼接 iterable 中的二进制序列,iterable 元素需为 bytes-like |
partition(sep) | 按 sep 首次出现拆分,返回 (前, sep, 后) 三元组 |
rpartition(sep) | 按 sep 最后出现拆分,返回三元组 |
removeprefix(prefix) | 移除开头的 prefix (3.9+ 新增),返回新对象 |
removesuffix(suffix) | 移除结尾的 suffix (3.9+ 新增),返回新对象 |
# 1. count:统计子序列出现次数
b = b'spam spam spam'
print(b.count(b'spam')) # 输出:3
print(b.count(115)) # 输出:3(115 是 's' 的 ASCII 码)# 2. find:查找子序列索引
print(b.find(b'spam', 5)) # 输出:6(从索引 5 开始找)# 3. join:拼接序列
parts = [b'hello', b' ', b'world']
print(b''.join(parts)) # 输出:b'hello world'# 4. removeprefix(3.9+)
b2 = b'TestHook'
print(b2.removeprefix(b'Test'))# 输出:b'Hook'
3.2. 基于 ASCII 的方法
以下方法假定数据为 ASCII 格式,仅处理 0~127 的字节,非 ASCII 字节保持不变,不适用于任意二进制数据(如图片、压缩文件):
方法 | 功能描述 |
---|---|
capitalize() | 首字节转为 ASCII 大写,其余转为小写 |
lower() / upper() | 所有 ASCII 字节转为小写/大写 |
swapcase() | ASCII 字节大小写互换(swapcase().swapcase() == 原对象 恒成立) |
isalnum() | 判断所有字节是否为 ASCII 字母/数字,且非空 |
isalpha() | 判断所有字节是否为 ASCII 字母,且非空 |
isdigit() | 判断所有字节是否为 ASCII 数字(0~9),且非空 |
# 1. capitalize:首字母大写
b = b'hello'
print(b.capitalize()) # 输出:b'Hello'# 2. upper:ASCII 大写(非 ASCII 字节不变)
b2 = b'hello \xe4\xb8\xad\xe6\x96\x87' # 包含中文
print(b2.upper()) # 输出:b'HELLO \xe4\xb8\xad\xe6\x96\x87'(中文不变)# 3. isalnum:判断 ASCII 字母/数字
print(b'isalnum()') # 输出:True(b'hello' 是 ASCII 字母)
print(b'hello123'.isalnum()) # 输出:True
print(b'hello!'.isalnum()) # 输出:False(包含 !)
3.3. printf 风格的二进制格式化
bytes
和 bytearray
支持通过 %
运算符进行类似 C 语言 printf
的格式化,适用于兼容旧代码,推荐优先使用 f-字符串(需先解码为 str
)。
核心转换符
转换符 | 功能描述 | 示例 |
---|---|---|
%d /%i | 十进制整数 | b'%d' % 123 → b'123' |
%x /%X | 十六进制整数(小写/大写) | b'%x' % 255 → b'ff' |
%b /%s | 二进制序列(bytes-like ) | b'%b' % b'abc' → b'abc' |
%c | 单个字节(整数或 bytes ) | b'%c' % 97 → b'a' |
%% | 输出 % 字符 | b'%%' → b'%' |
# 1. 基本格式化
print(b'Name: %b, Age: %d' % (b'Alice', 25)) # 输出:b'Name: Alice, Age: 25'# 2. 十六进制与整数
print(b'Hex: %x, Oct: %o' % (255, 8)) # 输出:b'Hex: ff, Oct: 10'# 3. 字典参数(映射键)
params = {b'name': b'Bob', b'age': 30}
print(b'Name: %(name)b, Age: %(age)d' % params) # 输出:b'Name: Bob, Age: 30'
4. memoryview 对象
memoryview
是 Python 中高效访问二进制数据的工具,通过“缓冲区协议”直接访问对象内存(如 bytes
、bytearray
、array.array
),无需复制数据,适合处理大二进制文件或高频数据操作。
4.1. memoryview 的创建与基础特性
4.1.1. 创建方式(memoryview(obj)
)
obj
需支持缓冲区协议,常见对象包括 bytes
、bytearray
、array.array
等。
# 1. 从 bytes 创建(只读,因 bytes 不可变)
b = b'abcdef'
mv1 = memoryview(b)
print(mv1) # 输出:<memory at 0x0000021F7A8D4350># 2. 从 bytearray 创建(可写,因 bytearray 可变)
ba = bytearray(b'abcdef')
mv2 = memoryview(ba)
print(mv2.readonly) # 输出:False(可写)
4.1.2. 序列特性(索引与切片)
memoryview
支持索引和切片,行为与 bytes
类似:
- 索引返回整数(对应字节数值);
- 切片返回子
memoryview
对象(不复制数据)。
mv = memoryview(b'abcdef')
# 1. 索引
print(mv[0]) # 输出:97(a 的 ASCII 码)
print(mv[-1]) # 输出:102(f 的 ASCII 码)# 2. 切片(返回子 memoryview,不复制)
sub_mv = mv[1:4] # 对应 b'bcd'
print(sub_mv) # 输出:<memory at 0x0000021F7A8D4410>
print(bytes(sub_mv)) # 输出:b'bcd'(转换为 bytes 查看内容)
4.2. memoryview 的核心方法
4.2.1. 数据转换(tolist()
、tobytes()
)
tolist()
:将内存数据转为 Python 列表(元素为整数,多维数据转为嵌套列表);tobytes()
:将内存数据转为bytes
(复制数据,适合持久化或传输)。
# 1. 一维数据
mv1 = memoryview(b'abc')
print(mv1.tolist()) # 输出:[97, 98, 99]
print(mv1.tobytes()) # 输出:b'abc'# 2. 多维数据(需先通过 cast 调整形状,见 4.2.2)
import array
arr = array.array('l', [1, 2, 3, 4]) # 'l' 表示长整数(4 字节/元素)
mv2 = memoryview(arr).cast('l', shape=[2, 2]) # 转为 2x2 多维视图
print(mv2.tolist()) # 输出:[[1, 2], [3, 4]]
4.2.2. 格式与形状转换(cast()
)
cast(format, shape=None)
方法将 memoryview
转换为新格式或形状,不复制数据,仅调整内存解读方式:
format
:目标格式(需为struct
模块支持的单一元素格式,如'B'
表示无符号字节、'l'
表示长整数);shape
:目标形状(默认为一维,如[2, 3]
表示 2 行 3 列的二维数据)。
# 1. 格式转换:长整数(l)→ 无符号字节(B)
import array
arr = array.array('l', [0x12345678]) # 1 个长整数(4 字节)
mv = memoryview(arr)
# 转为无符号字节格式(每个长整数拆为 4 个字节)
mv_byte = mv.cast('B')
print(mv_byte.tolist()) # 输出:[120, 86, 52, 18](小端序:0x78→120, 0x56→86...)# 2. 形状转换:一维 → 二维
buf = b'12345678' # 8 字节
mv2 = memoryview(buf).cast('B', shape=[2, 4]) # 2 行 4 列
print(mv2.tolist()) # 输出:[[49, 50, 51, 52], [53, 54, 55, 56]](1~8 的 ASCII 码)
4.2.3. 内存释放(release()
与上下文管理)
memoryview
会持有底层对象的引用,调用 release()
可手动释放内存(底层对象可恢复调整大小等能力);也可通过 with
语句自动释放。
# 1. 手动 release
ba = bytearray(b'abc')
mv = memoryview(ba)
mv.release() # 释放内存
# 释放后操作报错
# print(mv[0]) # 报错:ValueError: operation forbidden on released memoryview object# 2. with 语句自动释放
with memoryview(bytearray(b'abc')) as mv:print(mv[0]) # 输出:97(上下文内正常使用)
# 上下文外已释放
# print(mv[0]) # 报错:ValueError: operation forbidden on released memoryview object
4.3. memoryview 关键属性
memoryview
提供多个只读属性,用于查看内存数据的元信息:
属性 | 功能描述 |
---|---|
nbytes | 内存数据总字节数(product(shape) * itemsize ) |
readonly | 布尔值,表明内存是否只读(如 bytes 创建的 memoryview 为 True ) |
format | 数据格式(struct 风格,如 'B' 、'l' ) |
itemsize | 单个元素的字节数(如 'l' 对应 4 或 8 字节,取决于系统) |
shape | 数据形状(元组,如 (2, 4) 表示二维) |
ndim | 数据维度(整数,如 2 表示二维) |
import array
arr = array.array('l', [1, 2, 3, 4]) # 'l' 长整数,4 字节/元素
mv = memoryview(arr).cast('l', shape=[2, 2])print(mv.nbytes) # 输出:16(2*2*4 字节)
print(mv.readonly) # 输出:False(array 可修改)
print(mv.format) # 输出:'l'
print(mv.itemsize) # 输出:4(每个长整数 4 字节)
print(mv.shape) # 输出:(2, 2)
print(mv.ndim) # 输出:2
5. 总结
Python 二进制序列类型(bytes
、bytearray
、memoryview
)各有定位,适用场景不同:
bytes
:不可变二进制序列,适合存储固定二进制数据(如文件片段、网络数据包),支持哈希(可作为dict
键);bytearray
:可变二进制序列,适合动态修改二进制数据(如缓冲区、实时数据处理),但不支持哈希;memoryview
:高效内存访问工具,无需复制数据,适合处理大二进制数据或高频操作(如音视频处理、科学计算)。
使用时需注意:
bytes
和bytearray
的方法区分“通用二进制操作”和“ASCII 专属操作”,避免用于非 ASCII 二进制数据;memoryview
释放后不可再操作,推荐通过with
语句管理生命周期;- 二进制格式化优先使用 f-字符串(需先解码为
str
),仅兼容旧代码时使用%
运算符。