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

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

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


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


当前回答

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

再次运行生成器函数,重新启动生成: 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相同的结果和类似的性能。

其他回答

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

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

如果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)

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

没有重置迭代器的选项。迭代器通常在遍历next()函数时弹出。唯一的方法是在迭代迭代器对象之前进行备份。下面的检查。

创建包含0到9项的迭代器对象

i=iter(range(10))

遍历将弹出的next()函数

print(next(i))

将迭代器对象转换为list

L=list(i)
print(L)
output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

所以第0项已经跳出来了。此外,当我们将迭代器转换为list时,所有的项都会弹出。

next(L) 

Traceback (most recent call last):
  File "<pyshell#129>", line 1, in <module>
    next(L)
StopIteration

因此,在开始迭代之前,需要将迭代器转换为列表以备备份。 List可以用iter(< List -object>)转换为迭代器

我想为一个老问题提供一个不同的解决方案

class IterableAdapter:
    def __init__(self, iterator_factory):
        self.iterator_factory = iterator_factory

    def __iter__(self):
        return self.iterator_factory()

squares = IterableAdapter(lambda: (x * x for x in range(5)))

for x in squares: print(x)
for x in squares: print(x)

与list(iterator)相比,这样做的好处是它的空间复杂度是O(1),而list(iterator)是O(n)。缺点是,如果你只能访问迭代器,而不能访问产生迭代器的函数,那么你就不能使用这个方法。例如,这样做似乎是合理的,但它不会起作用。

g = (x * x for x in range(5))

squares = IterableAdapter(lambda: g)

for x in squares: print(x)
for x in squares: print(x)