append() 快在末尾追加且预留空间:CPython列表预分配冗余槽位,多数append仅更新指针和计数;扩容时复制全部元素一次,均摊时间复杂度为O(1)。

因为 append() 只写末尾、几乎不搬数据;insert() 一旦不在末尾,就得把后面所有元素挨个往后挪一位——这是 O(n) 的硬代价,不是“慢一点”,是数量级差异。
append() 快在哪儿:末尾追加 + 预留空间
CPython 的 list 底层是动态数组,但不是“刚好够用”就分配,而是故意多留空位。比如当前有 128 个槽位,长度到 129 才扩容;扩容时新容量 ≈ 当前长度 × 1.125 + 小常数(newsize >> 3 + 3 或 6)。这使得连续 append() 多数时候只是改指针、增计数,不用 memcpy。
- 即使触发扩容,也是复制全部现有元素一次,均摊下来仍是 O(1)
- 缩容只在长度 append() 完全不触发
- 语义清晰:
append()永远只动尾巴,无歧义
insert() 慢在哪儿:移动即成本,位置越靠前越致命
insert(i, x) 不管 i 是 0 还是 -1,只要不是末尾(即 i != len(lst)),Python 就必须从列表末尾开始,把索引 i 到 len(lst)-1 的所有元素逐个往后拷贝一位——这是纯 C 层的内存平移,无法跳过。
-
insert(0, x):移动全部 n 个元素 → O(n) -
insert(1, x):移动 n−1 个元素 → 仍 O(n) -
insert(-1, x):移动最后 1 个元素 → 还是得动,不能跳过边界检查和位移逻辑 -
insert(len(lst), x)等价于append(),但额外做了索引归一化、范围检查,实测慢 2–3 倍
别被“看起来一样”骗了:常见误用场景
很多人写 insert(0, x) 构建倒序列表,或在循环里反复 insert(i, x) 维护顺序——这些都不是“小优化问题”,是算法级陷阱。
立即学习“Python免费学习笔记(深入)”;
- 要头插?先
append(),最后list.reverse();或直接用collections.deque - 要有序插入?用
bisect.insort()(它内部仍调insert(),但至少减少查找开销) - 要批量插多个?收集到临时
list,再用切片赋值:lst[i:i] = temp_list,C 层优化过,比循环调insert()快一个数量级 - 真需要高频中间插入/删除?换结构:
sortedcontainers.SortedList或手写跳表
最易被忽略的一点:性能崩塌不是线性变慢,而是随数据量平方级恶化。10 万次 insert(0, x) 和 100 万次,耗时差距远不止 10 倍——因为每次移动的元素数本身就在增长。


















