itertools.chain适用于需单次遍历多个大列表的场景,它返回惰性迭代器,不复制数据、不占用额外内存;错误用法是直接传嵌套列表而不解包。

用 itertools.chain 避免一次性加载全部数据
当多个列表本身已存在于内存中,但总大小远超可用 RAM(比如每个 500MB,共 10 个),直接用 + 或 sum(lists, []) 会触发多次复制,瞬间吃光内存。此时真正需要的不是“合并成一个新列表”,而是“像一个列表那样迭代”。itertools.chain 正是为此设计:它不构造新容器,只返回一个惰性迭代器。
常见错误是误以为 chain 能“扁平化嵌套结构”——它只串联可迭代对象的一级,不递归展开。
- 正确用法:
for item in itertools.chain(list_a, list_b, list_c): ... - 错误用法:
itertools.chain(nested_list)(这只会迭代nested_list本身,不是它的子元素) - 若源头是生成器或文件流,优先用
chain接入,避免中间转成 list
合并前就用生成器替代列表存储
很多“超大列表”其实来自循环累积,比如读取多份 CSV、解析日志行、调用分页 API。这时候最省内存的做法,根本不是合并,而是从源头避免构建大 list。
典型场景:逐行处理 10 个大文件,每行是一个数据项。
立即学习“Python免费学习笔记(深入)”;
- 别写:
all_lines = []; for f in files: all_lines.extend(open(f).readlines()) - 改写为生成器函数:
def iter_all_lines(files): for f in files: yield from open(f),再用itertools.chain或直接 for 循环 - 注意
yield from在 Python 3.3+ 才支持;旧版本用for line in open(f): yield line
heapq.merge 仅适用于已排序列表的合并
如果所有输入列表本身已按同一规则升序(或降序)排列,heapq.merge 可以在 O(N log K) 时间内完成归并,且内存占用稳定在 O(K),K 是列表个数。它不会把所有元素拉进内存,而是维护一个 K 元堆,每次吐出最小值。
但一旦输入未排序,merge 结果就错,且无法检测——它照常运行,只是结果不符合预期。
- 适用:
heapq.merge(sorted_a, sorted_b, sorted_c, key=lambda x: x['ts']) - 不适用:
heapq.merge(unsorted_a, unsorted_b)(结果无序,且不是简单拼接) - 性能敏感时,确认排序成本是否低于全量加载:若排序需先转 list 再
sorted(),就失去意义
真要生成大 list?用 array.array 或 NumPy 替代 list
如果业务逻辑强制要求一个可随机索引的大容器(比如后续要多次 data[1234567]),那必须分配连续内存。此时 list 是最差选择:每个元素存指针,额外开销大,且碎片化严重。
根据数据类型切换底层结构:
- 纯数字(int/float):用
array.array('d', ...)(float64)或array.array('i', ...)(32 位 int),内存占用直降 3–5 倍 - 科学计算场景:NumPy
np.concatenate([arr1, arr2], axis=0)底层用 C 连续内存,比 Python list 快且省 - 注意:
array.array不支持嵌套或混合类型;NumPy 要求同 dtype,类型转换可能隐式拷贝
真正棘手的从来不是“怎么合并”,而是没想清楚下游到底需要什么访问模式——随机索引、单次遍历、还是流式处理。选错前提,再巧的合并技巧也救不了内存崩盘。


















