10

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

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

3 回答 3

25

这是因为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)
于 2012-07-19T21:26:50.263 回答
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))

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

于 2012-07-19T21:37:53.560 回答
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)

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

此外,如果您使用 pytoolz,您可能应该使用 cytoolz,它与编译成 C 的库相同。

于 2015-06-05T14:56:00.990 回答