Python中list_a + [value]为何比[*list_a, value]性能更优?
为什么
[*list_a, value]比list_a + [value]在Python 3.12.3中性能更差? 我原本以为list_b = [*list_a, value]生成带新增元素的新列表时,性能会比list_b = list_a + [value]更优——毕竟后者需要生成中间列表[value]。但在Python 3.12.3/Windows 10环境下的基准测试结果却完全相反:

测试代码
from timeit import timeit import random import matplotlib.pyplot as plt num_data_points = 1000 step = 10 methods = [ # ordered from slowest to fastest to make the key easier to read # "list_b = list_a.copy(); list_b += (value,)" # "list_b = list_a.copy(); list_b.append(value)" # "list_b = list(list_a); list_b.append(value)" "list_b = [*list_a, value]", "list_b = list_a + [value]", ] x = list(range(0, num_data_points * step, step)) y = [[] for _ in methods] for i in x: list_a = list(range(i)) random.shuffle(list_a) value = random.randint(0, num_data_points * step) setup = f"list_a = {list_a}; value = {value}" for method_index, method in enumerate(methods): y[method_index].append(timeit(method, setup=setup, number=800)) print(i, "out of", num_data_points * step) ax = plt.axes() for method_index, method in enumerate(methods): ax.plot(x, y[method_index], label=method) ax.set(xlabel="size of the list", ylabel="time (s) (lower is better)") ax.legend() ax.figure.canvas.get_default_filetype = lambda: 'svg' plt.show()
反汇编分析
我通过反汇编对比了两种方法的字节码:
反汇编代码
import dis list_a = [1, 2, 3] value = 4 def method_1(): list_b = [*list_a, value] def method_2(): list_b = list_a + [value] if __name__ == '__main__': dis.dis(method_1) dis.dis(method_2)
字节码结果
# list_b = [*list_a, value] 6 0 RESUME 0 7 2 BUILD_LIST 0 4 LOAD_GLOBAL 0 (list_a) 14 LIST_EXTEND 1 16 LOAD_GLOBAL 2 (value) 26 LIST_APPEND 1 28 STORE_FAST 0 (list_b) 30 RETURN_CONST 0 (None) # list_b = list_a + [value] 9 0 RESUME 0 10 2 LOAD_GLOBAL 0 (list_a) 12 LOAD_GLOBAL 2 (value) 22 BUILD_LIST 1 24 BINARY_OP 0 (+) 28 STORE_FAST 0 (list_b) 30 RETURN_CONST 0 (None)
性能差异的原因
从字节码和CPython底层实现来看,核心差异在于内存分配策略和操作效率:
[*list_a, value]的操作路径:- 先创建一个空列表(
BUILD_LIST 0) - 通过
LIST_EXTEND将list_a的所有元素添加到空列表中:这一步需要先获取list_a的长度,扩容空列表到对应大小,再复制元素 - 最后通过
LIST_APPEND添加单个元素:如果当前列表的容量刚好等于len(list_a),这一步会触发额外的内存扩容操作
- 先创建一个空列表(
list_a + [value]的操作路径:- 先创建长度为1的中间列表
[value](BUILD_LIST 1),这个操作的开销极小 - 调用列表的
__add__方法(BINARY_OP +):该方法会直接计算最终列表的总长度(len(list_a) + 1),一次性分配足够的内存,然后批量复制list_a和[value]的元素,全程不需要额外扩容
- 先创建长度为1的中间列表
另外,CPython对列表的__add__操作做了高度优化——针对两个列表的拼接,它会直接利用内存复制的方式完成,避免了逐个元素添加的开销;而LIST_EXTEND是通用的可迭代对象扩展逻辑,即使操作对象是列表,也会有额外的类型检查和迭代处理步骤,效率不如专用的__add__。
综上,虽然[*list_a, value]看似避免了中间列表的创建,但实际操作带来的内存扩容和通用迭代开销,远大于创建一个长度为1的小列表的成本,最终导致后者性能更优。
内容的提问来源于stack exchange,提问作者Danny Lin
相关产品推荐
相关产品推荐

