Python双生成器列表推导仅执行首次内层循环问题咨询
生成器在列表推导中的行为差异问题
我尝试用两个生成器创建列表推导:
g1 = (a for a in range(3)) g2 = (b for b in range(5)) l = [(i, j) for i in g1 for j in g2] print(l)
预期应该得到包含15个元组的列表,但实际只返回首次内层循环的结果:[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]。
但如果把生成器直接写在列表推导内部,结果就符合预期:
l = [(i, j) for i in (a for a in range(3)) for j in (b for b in range(5))] print(l)
这是我忽略了什么要点,还是Python的特性?
第一种写法的cProfile输出
ncalls tottime percall cumtime percall filename:lineno(function) 4 0.000 0.000 0.000 0.000 t.py:1(<genexpr>) 1 0.000 0.000 0.000 0.000 t.py:1(<module>) 6 0.000 0.000 0.000 0.000 t.py:2(<genexpr>) 1 0.000 0.000 0.000 0.000 t.py:3(<listcomp>) 1 0.000 0.000 0.000 0.000 {built-in method builtins.exec} 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
第二种写法的cProfile输出
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 t.py:1(<listcomp>) 1 0.000 0.000 0.000 0.000 t.py:1(<module>) 4 0.000 0.000 0.000 0.000 t.py:2(<genexpr>) 18 0.000 0.000 0.000 0.000 t.py:3(<genexpr>) 1 0.000 0.000 0.000 0.000 {built-in method builtins.exec} 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
解答
这是Python生成器的迭代耗尽特性导致的,属于语言本身的设计特性:
- 生成器是一次性迭代对象,一旦迭代到末尾(耗尽),后续再对它迭代只会返回空序列。
- 第一种写法中,
g2是预先定义好的单个生成器实例:
当外层循环第一次取i=0时,内层循环会把g2完全迭代耗尽;当外层循环取i=1和i=2时,g2已经没有元素可以迭代了,所以不会生成任何新元组。 - 第二种写法中,每次外层循环取新的
i时,都会重新创建一个新的生成器(b for b in range(5)),每个新生成器都是独立的、未被耗尽的,因此每次内层循环都能完整迭代5次,最终得到3*5=15个元组。
从cProfile的ncalls数据也能验证这一点:
- 第一种写法里,
t.py:2(<genexpr>)(对应g2)只被调用了6次(初始化+5次迭代),说明只迭代了一轮; - 第二种写法里,
t.py:3(<genexpr>)(对应内层生成器)被调用了18次,对应3次外层循环各创建一次生成器,每个生成器迭代5次,加上初始化次数,总调用数符合预期。
内容的提问来源于stack exchange,提问作者N. Mart
相关产品推荐
相关产品推荐

