我有一个由多重yield返回的生成器对象。准备调用这个生成器是相当耗时的操作。这就是为什么我想重复使用发电机几次。

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

当然,我正在考虑将内容复制到简单的列表中。有办法重置我的发电机吗?


请参见:如何提前查看Python生成器中的一个元素?


当前回答

来自tee的官方文件:

通常,如果一个迭代器使用了前面的大部分或所有数据 另一个迭代器启动时,使用list()比tee()更快。

所以在你的情况下最好使用list(iterable)。

其他回答

使用包装器函数来处理StopIteration

您可以为生成器生成函数编写一个简单的包装器函数,用于跟踪生成器耗尽的时间。它将使用生成器在迭代结束时抛出的StopIteration异常来完成此操作。

import types

def generator_wrapper(function=None, **kwargs):
    assert function is not None, "Please supply a function"
    def inner_func(function=function, **kwargs):
        generator = function(**kwargs)
        assert isinstance(generator, types.GeneratorType), "Invalid function"
        try:
            yield next(generator)
        except StopIteration:
            generator = function(**kwargs)
            yield next(generator)
    return inner_func

如上所述,当包装器函数捕获到StopIteration异常时,它只是重新初始化生成器对象(使用函数调用的另一个实例)。

然后,假设你定义了如下所示的生成器提供函数,你可以使用Python函数装饰器语法来隐式包装它:

@generator_wrapper
def generator_generating_function(**kwargs):
    for item in ["a value", "another value"]
        yield item
>>> 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

你可以使用itertools.cycle()来实现这一点。 您可以使用此方法创建一个迭代器,然后在迭代器上执行for循环,迭代器将对其值进行循环。

例如:

def generator():
for j in cycle([i for i in range(5)]):
    yield j

gen = generator()
for i in range(20):
    print(next(gen))

将生成20个数字,0到4重复。

医生说:

Note, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).

如果GrzegorzOledzki的答案还不够,那么可以使用send()来实现目标。有关增强生成器和yield表达式的更多详细信息,请参阅PEP-0342。

更新:参见itertools.tee()。它涉及到上面提到的内存与处理之间的一些权衡,但它可能比仅将生成器结果存储在列表中节省一些内存;这取决于你如何使用发电机。

现在可以使用more_itertools。Seekable(第三方工具),允许重置迭代器。

通过> pip Install more_itertools安装

import more_itertools as mit


y = mit.seekable(FunctionWithYield())
for x in y:
    print(x)

y.seek(0)                                              # reset iterator
for x in y:
    print(x)

注意:内存消耗会随着迭代器的增加而增加,所以要警惕大型迭代对象。