itertools模块是解决大规模循环内存和性能瓶颈的底层工具,返回惰性求值的迭代器而非列表,避免一次性加载全部数据。

itertools 模块不是“看起来更酷”的语法糖,而是解决大规模循环中内存和性能瓶颈的底层工具。它返回迭代器而非列表,所有操作都是惰性求值,这意味着你永远只在需要时才生成下一个值——这对处理百万级数据、流式输入或嵌套组合场景至关重要。
为什么 for x in combinations(items, 3) 不会爆内存
手动写三层嵌套循环或用列表推导式生成所有组合(如 [(a,b,c) for a in items for b in items for c in items])会在内存中一次性构造全部结果。而 combinations() 返回的是一个迭代器,每次调用 next() 或进入下一轮 for 循环才计算一个元组。
- 1000 个元素取 3 组合,总共有 ≈ 1.66 亿种可能,但
combinations()默认不存任何中间结果 - 你甚至可以对 10 万个字符串调用
combinations(..., 2),只要不把它转成list(),就不会触发内存告警 - 错误示范:
list(combinations(huge_list, 4))—— 这行代码极可能让进程被系统 OOM killer 杀掉
count() 和 islice() 配合才是可控的无限计数
直接用 count(1) 会无限产出数字,但没人真让它跑到底。关键在于用 islice() 安全截断:
from itertools import count, islice
# 取从 100 开始的连续 1000 个偶数,不构建大列表
evens = islice(count(100, 2), 1000)
for n in evens:
process(n) # 每次只 hold 一个 int
-
count()本身无终止逻辑,必须配合islice()、takewhile()或显式break - 误用
for i in count():而不加退出条件 → 程序卡死,CPU 拉满 -
islice(iterator, n)的n是“迭代次数”,不是“数值上限”——这点和range(n)完全不同
链式拼接多个文件/查询结果时,chain() 比 + 或 itertools.chain.from_iterable() 更直白
当你有多个可迭代对象(比如分页 API 返回的多批数据、多个 CSV 文件句柄),想统一遍历,chain(a, b, c) 是最轻量的选择:
from itertools import chain
# 假设这是三个生成器,每个 yield 1000 行
gen_a = read_csv('part1.csv')
gen_b = read_csv('part2.csv')
gen_c = read_csv('part3.csv')
<p>for row in chain(gen_a, gen_b, gen_c):
handle(row) # 内存里始终只有一行在流转
</p>
-
gen_a + gen_b + gen_c在 Python 中不合法(生成器不支持+) -
chain.from_iterable([gen_a, gen_b, gen_c])功能等价,但多一层嵌套,可读性略低 - 如果源是嵌套结构(如
[[1,2], [3,4], [5]]),才该用from_iterable();平铺已知数量的独立迭代器,用chain(a, b, c)更自然
组合类函数(combinations/permutations)的参数顺序容易反直觉
combinations(iterable, r) 的第二个参数 r 是“选几个”,不是“从第几个开始”。这个位置固定,且必须是整数:
- ✅ 正确:
combinations(['a','b','c'], 2)→('a','b'), ('a','c'), ('b','c') - ❌ 错误:
combinations(2, ['a','b','c'])→TypeError: 'int' object is not iterable -
r为 0 时返回空元组();r > len(iterable)时直接返回空迭代器(不报错,但for循环一次都不进) - 注意:所有组合函数默认不重复元素、不考虑顺序;要允许重复用
combinations_with_replacement()
真正难的不是记住函数名,而是判断什么时候不该把 itertools 结果转成 list——尤其当上游数据规模不确定时,那个 list(...) 往往就是压垮内存的最后一根稻草。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











