2012-07-19 83 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的同一個庫。

相關問題