我有一个由多重yield返回的生成器对象。准备调用这个生成器是相当耗时的操作。这就是为什么我想重复使用发电机几次。
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
当然,我正在考虑将内容复制到简单的列表中。有办法重置我的发电机吗?
请参见:如何提前查看Python生成器中的一个元素?
我有一个由多重yield返回的生成器对象。准备调用这个生成器是相当耗时的操作。这就是为什么我想重复使用发电机几次。
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
当然,我正在考虑将内容复制到简单的列表中。有办法重置我的发电机吗?
请参见:如何提前查看Python生成器中的一个元素?
当前回答
没有重置迭代器的选项。迭代器通常在遍历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>)转换为迭代器
其他回答
使用包装器函数来处理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 f():
def FunctionWithYield(generator_args):
code here...
return FunctionWithYield
现在你可以想做多少次就做多少次:
for x in f()(generator_args): print(x)
for x in f()(generator_args): print(x)
另一种选择是使用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()将生成器的结果物化到一个普通列表中并使用该列表。
好吧,你说你想多次调用一个生成器,但初始化是昂贵的…像这样的东西怎么样?
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