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

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

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


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


当前回答

现在可以使用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)

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

其他回答

现在可以使用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)

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

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

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

发电机不能倒带。您有以下选项:

再次运行生成器函数,重新启动生成: y = FunctionWithYield() 对于y中的x: print(x) y = FunctionWithYield() 对于y中的x: print(x) 将生成器结果存储在内存或磁盘上的数据结构中,您可以再次迭代: y = list(FunctionWithYield()) 对于y中的x: print(x) #可以再次迭代: 对于y中的x: print(x)

选项1的缺点是它会再次计算值。如果这是cpu密集型的,那么最终需要计算两次。另一方面,2的缺点是存储空间。整个值列表将存储在内存中。如果有太多的价值,那可能是不切实际的。

所以你有一个经典的内存和处理的权衡。我无法想象在不存储值或不重新计算它们的情况下倒带生成器的方法。

您也可以像其他答案所建议的那样使用tee,但是在您的情况下,它仍然会将整个列表存储在内存中,因此它将得到与选项2相同的结果和类似的性能。

如果你的生成器在某种意义上是纯的,它的输出只依赖于传递的参数和步长,并且你希望生成的生成器是可重新启动的,这里有一个排序代码片段可能很方便:

import copy

def generator(i):
    yield from range(i)

g = generator(10)
print(list(g))
print(list(g))

class GeneratorRestartHandler(object):
    def __init__(self, gen_func, argv, kwargv):
        self.gen_func = gen_func
        self.argv = copy.copy(argv)
        self.kwargv = copy.copy(kwargv)
        self.local_copy = iter(self)

    def __iter__(self):
        return self.gen_func(*self.argv, **self.kwargv)

    def __next__(self):
        return next(self.local_copy)

def restartable(g_func: callable) -> callable:
    def tmp(*argv, **kwargv):
        return GeneratorRestartHandler(g_func, argv, kwargv)

    return tmp

@restartable
def generator2(i):
    yield from range(i)

g = generator2(10)
print(next(g))
print(list(g))
print(list(g))
print(next(g))

输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
0
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1

它可以通过code对象来实现。下面是一个例子。

code_str="y=(a for a in [1,2,3,4])"
code1=compile(code_str,'<string>','single')
exec(code1)
for i in y: print i

1 2 3 4

for i in y: print i


exec(code1)
for i in y: print i

1 2 3 4