在Python中重置生成器对象

221 投票
18 回答
126660 浏览
提问于 2025-04-15 13:34

我有一个生成器对象,它是通过多次使用yield返回的。准备调用这个生成器的过程比较耗时。所以我想多次重用这个生成器。

y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)

当然,我也考虑过把内容复制到一个简单的列表里。有没有办法重置我的生成器呢?


另见: 如何在Python生成器中提前查看一个元素(peek)?

18 个回答

43

在编程中,有时候我们需要处理一些数据,比如从一个地方获取信息,然后在另一个地方使用这些信息。这个过程就像是把水从一个桶倒到另一个桶。

有些时候,我们会遇到一些问题,比如数据的格式不对,或者我们需要对数据进行一些处理才能使用。这就像是水桶的形状不一样,可能需要调整一下才能把水倒过去。

在这个过程中,我们可能会用到一些工具和方法,帮助我们更顺利地完成任务。这些工具就像是我们用来搬运水的桶和管子,能够让我们的工作变得简单。

总之,处理数据就像是搬运水,虽然有时候会遇到麻烦,但只要找到合适的方法和工具,就能顺利完成。

>>> def gen():
...     def init():
...         return 0
...     i = init()
...     while True:
...         val = (yield i)
...         if val=='restart':
...             i = init()
...         else:
...             i += 1

>>> g = gen()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.send('restart')
0
>>> g.next()
1
>>> g.next()
2
199

生成器不能被重置。你有以下几种选择:

  1. 再次运行生成器函数,重新开始生成:

    y = FunctionWithYield()
    for x in y: print(x)
    y = FunctionWithYield()
    for x in y: print(x)
    
  2. 把生成器的结果存储在内存或磁盘中的某种数据结构里,这样你可以再次遍历这些结果:

    y = list(FunctionWithYield())
    for x in y: print(x)
    # can iterate again:
    for x in y: print(x)
    

选择1的缺点是,它会重新计算这些值。如果这个计算很耗费CPU资源,那你就相当于计算了两次。另一方面,选择2的缺点是存储问题。所有的值都会存储在内存中。如果值太多,这样做就不太实际了。

所以你面临经典的内存与处理速度的权衡。我想不出有什么办法可以在不存储值或不重新计算的情况下重置生成器。

你也可以使用tee,正如其他回答所建议的那样,但在你的情况下,这样做仍然会把整个列表存储在内存中,所以结果和性能与选择2是一样的。

163

另一个选择是使用 itertools.tee() 这个函数来创建你生成器的第二个版本:

import itertools
y = FunctionWithYield()
y, y_backup = itertools.tee(y)
for x in y:
    print(x)
for x in y_backup:
    print(x)

从内存使用的角度来看,如果原始的迭代可能不会处理所有的项目,这样做可能会更有好处。

撰写回答