bytes不可变,无append和insert方法,调用会报AttributeError;bytearray可变,append均摊O(1),insert触发局部内存移动,扩容策略为比例增长。

bytes 的 append 和 insert 操作根本不存在
bytes 是不可变类型,任何看似“追加”或“插入”的操作(比如 +=、b + b2、bytes.insert())都会触发新对象创建。所谓 bytes.append() 会直接报 AttributeError: 'bytes' object has no attribute 'append';同理 insert 方法也不存在。这不是性能问题,是语法错误——你连调用都调不通。
常见误操作包括:
- 把
bytes当成bytearray用,结果在运行时崩掉 - 用
+=拼接小段bytes,误以为是原地修改,实际每次都在分配新内存并拷贝全部字节 - 在循环里反复
b = b + new_part,时间复杂度退化为 O(n²),尤其当总长度增长到几 MB 以上时明显卡顿
bytearray.append() 是真正的 O(1) 均摊操作
bytearray 是可变序列,append() 在底层使用动态数组策略:预分配额外空间,避免每次插入都 realloc。只要未触及容量上限,单次 append() 不触发整块内存拷贝。
实测要点:
- 初始
bytearray()容量通常为 0,首次append后会分配约 16 字节;后续扩容按比例增长(CPython 中约为 1.125 倍) - 大量追加时,总拷贝次数远少于追加次数——例如追加 1000 个字节,实际可能只发生 5–8 次 realloc + memcpy
- 若提前知道最终大小,用
bytearray(n)预分配再逐字节赋值(ba[i] = x),可彻底避免扩容拷贝
示例对比:
ba = bytearray()
for i in range(1000):
ba.append(i & 0xFF) # 快,均摊 O(1)
b = b''
for i in range(1000):
b += bytes([i & 0xFF]) # 慢,O(n²),每轮都复制前面所有字节
bytearray.insert() 触发局部内存移动,代价明确
bytearray.insert(i, x) 必须将索引 i 及之后的所有字节向后平移一位,本质是 memmove(dst=i+1, src=i, n=len-i)。它不涉及 realloc(除非容量不足),但移动成本与插入位置后的长度成正比。
关键事实:
- 在开头
insert(0, x)最慢:需移动全部现有数据 - 在末尾
insert(len(ba), x)等价于append(),不移动数据,仅检查容量 - 频繁中间插入应改用
list[bytes]缓存再b''.join(),或改用io.BytesIO(其write()是追加语义,getbuffer()可零拷贝转bytearray)
内存拷贝是否发生,看底层 PyBufferProcs 和 ob_size/allocated
判断一次操作是否拷贝内存,不能只看函数名,得看 CPython 源码中对应方法是否调用了 PyMem_Realloc 或 memmove。比如:
-
bytearray.extend(iterable):若容量足够,直接 memcpy 新数据到尾部;否则先 realloc 再 memcpy -
del ba[i:j]:不 realloc,只 memmove 后续数据覆盖被删区域 -
ba[:] = other:若len(other) ,就地覆盖;否则先 realloc
真正难察觉的坑是:某些看似“安全”的操作(如切片赋值 ba[10:10] = b'xyz')在容量不足时仍会 realloc —— 这时候你以为只是插几个字节,实际背后发生了整块拷贝。










