应先收集所有小数组到列表再一次性拼接,避免逐次调用np.concatenate导致O(n²)内存拷贝;推荐用list.append后单次np.concatenate,或内存受限时用np.memmap预分配。

直接用 np.concatenate 逐个拼接数万个数组,内存和速度都会崩——这不是写法问题,是设计误用。
为什么 np.concatenate 在小数组批量拼接时很慢?
每次调用 np.concatenate 都会分配新内存、复制全部已有数据。假设有 10,000 个长度为 32 的 float32 数组,逐次拼接会产生约 O(n²) 级别的内存拷贝量,实际耗时可能比预期高 10–100 倍。
- 底层不复用缓冲区,每次都是“读旧 → 分配新 → 写入”三步全走
- Python 层循环调用开销叠加(尤其在 CPython 中)
- 若数组来自不同 dtype 或 shape,还会触发隐式转换或广播检查,进一步拖慢
推荐做法:先收集再一次性拼接
把所有小数组先存进 Python 列表,等全部就绪后,用单次 np.concatenate 或 np.vstack/np.hstack 完成合并。这是最简单且通常最优的方案。
- 列表 append 是 C 层优化过的,开销极低
-
np.concatenate接收 list of arrays 时,内部会预计算总长度并一次性分配内存 - 确保所有数组 shape 兼容:例如拼接列向量时,全部是
(n, 1);拼接行向量则统一为(1, n) - 如果 dtype 不一致,提前统一(如用
arr.astype(np.float32)),避免运行时隐式转换
示例:
立即学习“Python免费学习笔记(深入)”;
arrays = []
for i in range(10000):
x = generate_small_array() # 返回 shape=(32,) 的 ndarray
arrays.append(x)
result = np.concatenate(arrays, axis=0) # 一次完成,shape=(320000,)当内存受限或需流式处理时,用 np.memmap 预分配
如果拼接结果太大,放不下内存(比如最终 >10GB),又不想分块写磁盘再读回,可以用 np.memmap 提前映射一块文件-backed 内存区域,然后按偏移写入。
- 先算出总长度:
total_size = sum(len(a) for a in all_arrays) - 创建 memmap:
out = np.memmap("temp.dat", dtype=np.float32, mode="w+", shape=(total_size,)) - 用累积索引写入:
start = 0; for a in arrays: out[start:start+len(a)] = a; start += len(a) - 后续可直接当普通 ndarray 用(只要不 close 或 del)
- 注意:memmap 不支持 resize,必须预先知道总尺寸
别碰 np.append 和 += 拼接
np.append 底层就是调用 np.concatenate,而且额外多一次 copy —— 它返回新数组,原数组不变,纯属语义糖衣炮弹。
-
arr = np.append(arr, new_part)每次都复制整个arr,复杂度爆炸 -
arr += ...只适用于原地运算,不能用于拼接(维度不匹配会报ValueError: operands could not be broadcast together) - 哪怕用
list+extend再转np.array,也比np.append循环快得多
真正卡住性能的,往往不是算法本身,而是没意识到 np.concatenate 的输入形态对执行路径的影响——传 list 比传 tuple 稍慢但可忽略,传 generator 则会触发降级路径,务必避免。


















