2012-07-19 28 views
9

有人请解释使用生成器的嵌套循环的行为吗?这是一个例子。发生器的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) 

回答

22

这是因为b发生器在outer 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) 
+0

啊哈!我没有注意到发电机的耗尽。非常感谢。 – phantomile 2012-07-19 21:34:17

8

@lazyr已经出色地回答了这一点,但我想指出的是参考使用嵌套发电机当它是值得了解itertools.product ...

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

或者(如果你有很多瓦尔斯的):

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

它的(恕我直言)易读,而且避免过深的缩进。

0

itertools.product最适合这个例子。但是在迭代过程中你可能需要更多的选择。这里是一个办法仍然可以在你的榜样的产品不使用产品的方法:

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

还有,我用itertoolz.concat从pytoolz功能助手库来简化/扁平化这样的情况下。的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-loops,这可能很好。

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

相关问题