You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何向Python列表追加元素比修改NumPy数组元素更快?

为什么Python列表append比预分配NumPy数组索引赋值更快?

你的测试代码如下:

import numpy as np
import time

N = 10**8

l = []
L = np.zeros(N)

t1 = time.time()
for i in range(N):
    l.append(1)
t2 = time.time()

print("Adding into list took:", t2 - t1, "s")

t1 = time.time()
for i in range(N):
    L[i] = 1
t2 = time.time()

print("Adding into array took:", t2 - t1, "s")

测试结果:

Adding into list took: 12.784213542938232 s
Adding into array took: 19.65919280052185 s

出现这种反直觉结果的核心原因如下:

1. 列表append的底层预分配优化

Python列表的扩容并非每次追加都重新分配内存,而是采用**平摊O(1)**的预分配策略:每次内存不足时,会额外分配当前容量1.5倍左右的空间(不同实现略有差异)。这意味着绝大多数append操作只是简单的内存写入,开销极低,而且append是Python原生的C级实现,没有额外的类型转换或复杂检查。

2. NumPy数组循环赋值的额外开销

虽然NumPy数组是连续预分配的内存,但Python层面的循环中,每次L[i] = 1都会产生额外开销:

  • 需要将Python的整数对象1转换为NumPy数组默认的float64类型(np.zeros默认 dtype 是float),这个类型转换会消耗时间
  • NumPy的索引操作自带边界检查、类型校验等逻辑(为了支持多维数组、切片等复杂功能),即使是单元素索引,这些检查也会带来额外开销
  • Python与NumPy底层C代码之间的桥接调用,每次循环都会产生固定的开销

3. NumPy的正确用法:向量化操作

NumPy的优势从来不是Python层面的循环,而是向量化批量操作。如果把循环赋值改成向量化赋值,速度会碾压列表append:

# 替换循环赋值为向量化操作
t1 = time.time()
L[:] = 1
t2 = time.time()
print("Adding into array via vectorization took:", t2 - t1, "s")

运行这个代码,你会发现耗时通常在0.1秒以内,远快于列表的12秒左右。

4. 数据类型的影响

如果将NumPy数组指定为整数类型(np.zeros(N, dtype=int)),循环赋值的速度会有所提升,但依然不如列表append——因为Python循环对NumPy的操作始终存在桥接开销,而列表append是原生优化的操作。

总结:Python列表的append在循环中表现优异,是因为其底层预分配机制和原生C实现的低开销;而NumPy在Python循环中表现拉胯,是因为每次索引赋值都有额外的类型转换和桥接成本,NumPy的正确打开方式是避免Python循环,使用向量化批量操作。

内容的提问来源于stack exchange,提问作者Syrocco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 16:45:26