没有办法“重置”生成器。但是,您可以使用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) 所有后续访问都将在系列中产生 later 值,并且 b) 一旦它为“空”,我们就得到了该系列中的所有项目恰好一次。我们操纵z 状态的能力越强,就越难以推理它,所以我们最好避免违反这两个承诺的情况。
当然,正如 Joel Cornett 在下面指出的那样,可以编写一个通过 send 方法接受消息的生成器;并且可以编写一个可以使用send 重置的生成器。但请注意,在这种情况下,我们所能做的就是发送消息。我们不能直接操纵生成器的状态,因此对生成器状态的所有更改都是明确定义的(由生成器本身 - 假设它是正确编写的!)。 send 确实是为了实现coroutines,所以我不会将它用于此目的。日常生成器几乎从不使用发送给它们的值做任何事情——我认为这正是我上面给出的原因。