使用生成器的Python嵌套循环不起作用(在某些情况下)?

时间:2012-07-19 21:23:36

标签: python generator nested-loops

请问有人请使用生成器解释嵌套循环的行为吗?这是一个例子。

a = (x for x in range(3))
b = (x for x in range(2))
for i in a:
    for j in b:
        print (i,j)

由于某种原因,在第一次迭代后不评估外部循环。结果是,

(0, 0)
(0, 1)

另一方面,如果将生成器直接插入循环中,它就能达到我的预期效果。

for i in (x for x in range(3)):
    for j in (x for x in range(2)):
        print (i,j)

给出所有3x2对。

(0, 0)
(0, 1)
(1, 0)
(1, 1)
(2, 0)
(2, 1)

3 个答案:

答案 0 :(得分:22)

这是因为b生成器在外部for循环的第一次迭代期间耗尽。后续迭代实际上会有一个空的内部循环(如for x in ()),因此内部的内容永远不会被执行。这给人以错误的印象,就是外环在某种程度上失败了。

您的第二个示例有效,因为每个外部循环都会重新创建内部生成器。要修复你的第一个例子,你必须这样做:

a = (x for x in range(3))
for i in a:
    b = (x for x in range(2))
    for j in b:
        print (i,j)

答案 1 :(得分:8)

@lazyr已经很好地回答了这个问题,但是我想指出,在使用嵌套生成器时,值得了解itertools.product ...

for i, j in itertools.product(range(3), range(2)):
    print (i, j)

或(如果你有批次的vals):

for vals in itertools.product(range(45), range(12), range(3)):
    print (sum(vals))

它是(恕我直言)可读并避免过度缩进。

答案 2 :(得分:0)

itertools.product最适合这个例子。但是在迭代期间您可能需要更多选项。以下是在不使用产品方法的情况下仍然可以在示例中获取产品的一种方法:

a = (range(2) for x in range(3))
for i in a:
    for j in i:
        print (i,j)

另外,我使用pytoolz功能助手库中的itertoolz.concat来简化/展平这样的案例。 concat就像itertools.chain,但是只接受一个参数,它产生的迭代器会被解开:

from pytoolz import itertoolz
a = (((x,y) for y in range(2)) for x in range(3))
for i,j in itertoolz.concat(a):
    print (i,j)

因此,上述内容看起来不如产品方法可读,但允许在每个循环级别进行更细粒度的转换/过滤。当然,在最终的迭代逻辑中你没有嵌套的for循环,这可能很好。

另外,如果你使用pytoolz,你可能应该使用cytoolz,它是编译成C的同一个库。