list.append() vs list += [x] 在性能与内存拷贝上的细微差别

6次阅读

append() 原地添加单个元素,时间复杂度 O(1);+= 等价于 extend(),要求右侧可迭代,会迭代展开添加。二者均只复制引用、不深拷贝,但语义和错误模式不同。

list.append() vs list += [x] 在性能与内存拷贝上的细微差别

list.append() 是原地添加单个元素,不触发序列解包

list.append() 直接在列表末尾插入一个对象引用,时间复杂度 O(1)(均摊),且不涉及任何拷贝或迭代。它只修改 list 的内部指针数组,扩容时才发生一次内存重分配。

常见误用是把它当批量操作:比如 lst.append([1, 2, 3]) 会把整个列表作为一个元素嵌套进去,而不是展开。这点和 += 完全不同。

实操建议:

  • 明确只加一个元素时,优先用 append() —— 语义清晰、开销最小
  • 不要用 append() 替代 extend()+= 来“假装”批量添加
  • 如果目标是追加多个值,用 extend()+= [x, y, z],而非循环调用 append()

list += [x] 实际调用 __iadd__,等价于 extend() 而非 append()

+= 对列表不是简单赋值叠加,而是触发 list.__iadd__() 方法,该方法内部行为与 extend() 完全一致:它迭代右侧对象(必须是可迭代的),逐个调用 append()。所以 lst += [x]lst.extend([x]) 行为相同,但比 lst.append([x]) 多一层迭代和类型检查。

关键细节:

  • lst += [x] 要求右侧是可迭代对象[x] 是合法的,但 lst += x(x 是 int)会报 TypeError: 'int' Object is not iterable
  • 虽然语义上像“加一个元素”,但它仍走迭代路径,有微小额外开销(创建迭代器、检查 __iter__
  • extend() 一样,若右侧很大,+= 会预估容量并一次性扩容,比循环 append() 更高效

性能差异在小数据量下几乎不可测,但语义和错误倾向完全不同

对单个元素,append()+= [x] 快约 1.2–1.5×(Cpython 3.12 测得,百万次循环差 ~40ms)。但这不是瓶颈点。真正影响开发体验的是错误模式:

  • lst += x(x 是 int)→ 立即报错,提示明确
  • lst.append([x]) → 不报错,但产生意外嵌套结构,后期引发 TypeError 或逻辑 bug,更难定位
  • += 支持任意可迭代对象lst += "ab" 会拆成 ['a', 'b']append() 则把字符串当一个整体

所以选哪个,主要看你要“加一个东西”还是“加一批东西的展开结果”。

内存拷贝层面:两者都不复制元素对象,只复制引用

无论是 append() 还是 += [x],都只是把对象的引用追加到列表的指针数组中,不会对 x 本身做深拷贝或浅拷贝。如果你改了 x 的内容(比如它是 list 或 dict),所有持有该引用的地方都会看到变化。

容易被忽略的一点:

  • += 在扩容时可能触发一次底层内存 realloc,而 append() 在同样扩容时机也做同样的事 —— 所以二者在内存分配行为上没有本质区别
  • += 若右侧是生成器(如 lst += (x for x in range(1000))),会强制完全消费并缓存全部元素,可能导致临时内存上升;append() 没这问题,因为它根本不接受生成器作为参数
text=ZqhQzanResources