2017-01-10 16 views
19

用下面的代碼:嵌套發電機表情表現異常

A = [1, 2] 
B = [-2, -1] 
C = [-1, 2] 
D = [0, 2] 

ab = (a + b for a in A for b in B) 
cd = (c + d for c in C for d in D) 
abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd) 

len(abcd)預計16,但它實際上是4。如果我用列表理解來代替,問題就會消失。這是爲什麼?

回答

26

只能乘坐火車產生一次,到達目的地後,沒有更多的遊樂設施。在你的情況下,cd發生器耗盡,然後不能再次迭代。

list對象,而另一方面,創建每次你叫iter他們(其中for循環隱含爲您做的)一個獨立的迭代器對象

print(iter([1, 2, 3])) 
# <list_iterator at 0x7f18495d4c88> 

,併產生一個新的迭代器你可以使用。發生這種情況隨時iter被調用;由於每次都會生成一個新對象,因此可以多次查看列表。多種遊樂設施!

總之,如果你變化cd是一個列表(在一般情況下,將通過多次迭代的對象):

ab = (a + b for a in A for b in B) 
cd = [c + d for c in C for d in D] # list-comp instead 

將通過創建新的迭代器得到想要的結果從cd對象的每一個元素在ab

abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd) 
print(len(list(abcd))) 
# 16 
當然

您可以通過使用product從01做到這一點,但這超出了爲什麼會發生這一點。

14

我想這是因爲你只能迭代一次發生器。所以在第一次循環完成e_cd之後,這將不會在另一個外部循環迭代中產生任何東西。

12

當發電機沒有其他值要返回時,它會引發一個StopIteration異常。這是他們如何表示他們已完成的信號。由於沒有內置的方法來重置生成器,因此當您從生成器創建多階段生成器時,它將在遇到的第一個StopIteration處停止,而不是像列表類對象那樣導致子生成器循環。

itertools.product()能產生預期的效果(repl.it here):

import itertools 

A = [1, 2] 
B = [-2, -1] 
C = [-1, 2] 
D = [0, 2] 

ab = (a + b for a in A for b in B) 
cd = (c + d for c in C for d in D) 
abcd = (e_ab + e_cd for e_ab, e_cd in itertools.product(ab,cd)) 
+0

這也可以寫成map(sum,itertools.product(A,B,C,D))'。 – deltab