bytes不可变,无append和insert方法,调用会报AttributeError;bytearray可变,append均摊O(1),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() 在底层使用动态数组策略:预分配额外空间,避免每次插入都 realloc。只要未触及容量上限,单次 append() 不触发整块内存拷贝。
实测要点:
bytearray() 容量通常为 0,首次 append 后会分配约 16 字节;后续扩容按比例增长(CPython 中约为 1.125 倍)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(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)判断一次操作是否拷贝内存,不能只看函数名,得看 CPython 源码中对应方法是否调用了 PyMem_Realloc 或 memmove。比如:
bytearray.extend(iterable):若容量足
del ba[i:j]:不 realloc,只 memmove 后续数据覆盖被删区域ba[:] = other:若 len(other) ,就地覆盖;否则先 realloc
真正难察觉的坑是:某些看似“安全”的操作(如切片赋值 ba[10:10] = b'xyz')在容量不足时仍会 realloc —— 这时候你以为只是插几个字节,实际背后发生了整块拷贝。