itertools.product 生成笛卡尔积迭代器,需解包变量列表、注意空输入与 repeat 用法,惰性求值但易内存爆炸,适合纯组合场景而非复杂控制流。

itertools.product 生成笛卡尔积的基本用法
itertools.product 是 Python 标准库中生成笛卡尔积最直接的方式,它返回一个迭代器,不是列表。这意味着如果你需要多次遍历或索引访问,得先转成 list,否则第二次调用会得到空结果。
最常见写法是传入多个可迭代对象:
from itertools import product result = product([1, 2], ['a', 'b']) print(list(result)) # [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
- 参数顺序决定元组内元素顺序:第一个可迭代对象的元素在元组左侧
- 如果只传一个可迭代对象,需显式用
repeat参数,比如product([0, 1], repeat=3)等价于product([0, 1], [0, 1], [0, 1]) - 传空列表时,
product返回空迭代器(不报错),但list(product([]))得到[];而product([], repeat=2)也返回空——这点容易误以为有“全零组合”
处理嵌套结构或动态参数列表
当你要笛卡尔积的序列本身是变量(比如来自配置、用户输入或函数返回),不能直接写死参数,得用解包。
错误写法:product([[1,2], ['a','b']]) —— 这只会把整个列表当做一个参数,结果是 ([1, 2],) 和 (['a', 'b'],) 两个单元素元组。
立即学习“Python免费学习笔记(深入)”;
正确做法是用 * 解包:
sets = [[1, 2], ['a', 'b'], [True, False]] result = product(*sets) print(list(result)) # 8 个三元组
- 如果
sets是空列表[],product(*sets)等价于product(),返回一个含空元组的迭代器:list(product()) == [()],这常被忽略 - 解包前务必确认每个子项确实是可迭代对象;传入
None或整数会立刻抛TypeError: 'int' object is not iterable
内存与性能需要注意的边界情况
product 是惰性求值的,但笛卡尔积大小是各参数长度的乘积。哪怕只是 product(range(100), range(100), range(100)),也有 100 万次组合——转成 list 会瞬间吃掉几百 MB 内存。
- 优先用迭代器消费结果,比如配合
for循环或next()逐个取值 - 避免无脑
list(product(...)),尤其参数含大列表、文件行、数据库查询结果时 - 如果只需要前 N 个结果,用
itertools.islice(product(...), N),比生成全部再切片高效得多 -
product本身没有内置去重或过滤逻辑,如需条件筛选,请在外层加filter()或用生成器表达式,别试图改写product
和嵌套 for 循环对比:什么时候不该用 product
简单两层循环且逻辑复杂时,硬套 product 反而降低可读性。例如需要在第二层迭代中依赖第一层变量的状态、或中途 break/continue 控制流。
下面这段代码用 product 就很别扭:
# 想找第一个 a + b == 10 的组合,且 b 必须是偶数
for a in range(5):
for b in range(5):
if a + b == 10 and b % 2 == 0:
print(a, b)
break
else:
continue
break-
product无法自然支持这种带状态/提前退出的嵌套逻辑 - 若每轮循环体里还要做 I/O、异常处理或修改外部变量,用显式
for更清晰可控 -
product适合“纯数据组合”场景,比如生成测试用例、枚举参数组合、构建路径字符串等
笛卡尔积爆炸是静默问题:代码能跑通,但数据量稍大就卡死或 OOM。实际写之前,先粗略算下 math.prod(len(s) for s in sets) 是多少,比调试时才发现来得实在。


















