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

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

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


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


当前回答

你可以使用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).

其他回答

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

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

好吧,你说你想多次调用一个生成器,但初始化是昂贵的…像这样的东西怎么样?

class InitializedFunctionWithYield(object):
    def __init__(self):
        # do expensive initialization
        self.start = 5

    def __call__(self, *args, **kwargs):
        # do cheap iteration
        for i in xrange(5):
            yield self.start + i

y = InitializedFunctionWithYield()

for x in y():
    print x

for x in y():
    print x

或者,你也可以创建自己的类,遵循迭代器协议,并定义某种“reset”函数。

class MyIterator(object):
    def __init__(self):
        self.reset()

    def reset(self):
        self.i = 5

    def __iter__(self):
        return self

    def next(self):
        i = self.i
        if i > 0:
            self.i -= 1
            return i
        else:
            raise StopIteration()

my_iterator = MyIterator()

for x in my_iterator:
    print x

print 'resetting...'
my_iterator.reset()

for x in my_iterator:
    print x

https://docs.python.org/2/library/stdtypes.html#iterator-types http://anandology.com/python-practice-book/iterators.html

另一种选择是使用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)

从内存使用的角度来看,如果原始迭代可能不处理所有的项,这可能是有益的。

可能最简单的解决方案是将昂贵的部分包装在一个对象中,并将其传递给生成器:

data = ExpensiveSetup()
for x in FunctionWithYield(data): pass
for x in FunctionWithYield(data): pass

这样,就可以缓存昂贵的计算。

如果您可以同时将所有结果保存在RAM中,那么可以使用list()将生成器的结果物化到一个普通列表中并使用该列表。

>>> 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