You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:25:39