使用以下代码:
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
。如果我使用列表理解,问题就会消失。那是为什么?
答案 0 :(得分:27)
你只能乘坐发电机列车一次,到达目的地后,不再乘坐。在您的情况下,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
中的itertools
来实现此目的,但这超出了发生这种情况的原因。
答案 1 :(得分:16)
我认为这是因为你只能迭代生成器一次。因此,在第一次循环e_cd
之后,这将不会在外部循环的另一次迭代中产生任何内容。
答案 2 :(得分: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))