没有办法“重置”发电机。但是,您可以使用itertools.tee
“复制”迭代器。
>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]
这涉及缓存值,因此仅当您以大致相同的速率迭代两个可迭代对象时才有意义。(换句话说,不要像我这里那样使用它!)
另一种方法是传递生成器函数,并在您想要迭代它时调用它。
def gen(x):
for i in range(x):
yield i ** 2
def make_two_lists(gen):
return list(gen()), list(gen())
但是现在您必须在传递参数时将参数绑定到生成器函数。你可以使用lambda
它,但很多人觉得lambda
丑陋。(但不是我!YMMV。)
>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
我希望不用说,在大多数情况下,最好只是列出并复制它。
此外,作为解释这种行为的更一般的方式,请考虑这一点。生成器的目的是产生一系列值,同时在迭代之间保持某种状态。现在,有时,您可能想要执行以下操作,而不是简单地迭代生成器:
z = zip(a, b)
while some_condition():
fst = next(z, None)
snd = next(z, None)
do_some_things(fst, snd)
if fst is None and snd is None:
do_some_other_things()
假设这个循环可能会也可能不会用尽z
。现在我们有一个处于不确定状态的生成器!因此,在这一点上,以明确定义的方式限制生成器的行为是很重要的。尽管我们不知道生成器在其输出中的位置,但我们知道 a) 所有后续访问都将在系列中生成后面的值,并且 b) 一旦它为“空”,我们就准确地获得了系列中的所有项目一次。我们操纵 的状态的能力越强z
,推理它的难度就越大,所以我们最好避免违反这两个承诺的情况。
当然,正如 Joel Cornett 在下面指出的那样,可以编写一个通过该send
方法接受消息的生成器;并且可以编写一个可以使用send
. 但请注意,在这种情况下,我们所能做的就是发送一条消息。我们不能直接操纵生成器的状态,因此对生成器状态的所有更改都是明确定义的(由生成器本身 - 假设它是正确编写的!)。send
真的是为了实现coroutines,所以我不会为此目的使用它。日常生成器几乎从不使用发送给它们的值做任何事情——我认为这正是我上面给出的原因。