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



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

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)

这是因为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)

@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))

它具有(IMHO)可读性,避免了过多的缩进。

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)

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

此外,如果您使用pytoolz,您可能应该使用cytolz,它是编译到C.中的同一个库

最新更新