list.append() 在大量数据下变慢是因为动态数组扩容时需 realloc 和 memcpy;预分配(如 [None] * N)或列表推导式可避免频繁拷贝,但仅适用于长度可预测场景。

为什么 list.append() 在大量数据下会变慢?
Python 的 list 底层是动态数组,每次容量不足时会重新分配内存并复制旧元素。扩容策略是「乘数增长」(CPython 中约 1.125 倍),看似平滑,但频繁触发仍带来显著拷贝开销——尤其当你要追加几十万次时,可能触发上百次 realloc 和 memcpy。
预分配容量:用 list comprehension 或 * 运算符初始化
如果你能预估最终长度(比如读取固定行数的文件、已知循环次数),直接创建定长列表比反复 append 快得多。关键不是“避免 append”,而是“避免在 tight loop 里无脑 append”。
- 已知长度 N:用
[None] * N或[0] * N初始化,再按索引赋值(res[i] = x) - 需要生成值:优先用列表推导式
[func(i) for i in range(N)],它由解释器一次性分配、无中间扩容 - 慎用
list.extend()替代多次append(),但前提是你要把一批数据攒好再塞进去
什么情况下不能/不该预分配?
预分配只适用于「长度可预测」的场景。一旦逻辑依赖运行时判断(比如过滤条件不确定、网络响应长度未知),硬预分配反而浪费内存或引发 IndexError。
- 流式处理(如逐行读文件 + 条件过滤):老实用
append(),或改用生成器 +itertools.chain缓冲小批量 - 嵌套结构拼接(如构建树节点):预分配索引难对齐,不如用
collections.deque做中间容器(deque.append()均摊 O(1),且不涉及连续内存拷贝) - 小数据量(
用 sys.getsizeof() 验证扩容代价
实际观察列表内存变化,比理论更直观。执行以下代码能清楚看到扩容拐点:
立即学习“Python免费学习笔记(深入)”;
import sys
l = []
for i in range(100):
l.append(i)
if i in (0, 1, 4, 8, 16, 32, 64, 99):
print(f"{i:2d} items → {sys.getsizeof(l):3d} bytes")
输出中你会看到字节数并非线性增长,而是在某些长度(如 64→99)之间突然跳升——那就是扩容发生的证据。这种跳跃越频繁,你的 CPU 越忙于搬数据。
真正影响性能的往往不是单次扩容,而是你没意识到自己正在写一个隐式 O(n²) 的循环:外层遍历 n 次,内层 append 触发平均 O(n) 的拷贝。把预分配意识带进设计阶段,比事后优化 append 调用更有效。



















