Dew*_*wfy 133 python yield generator
我有多个yield返回的生成器对象.准备调用这台发电机是相当费时的操作.这就是我想多次重用发生器的原因.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
Run Code Online (Sandbox Code Playgroud)
当然,我正在考虑将内容复制到简单的列表中.
nos*_*klo 133
发电机不能倒带.您有以下选择:
再次运行生成器函数,重新启动生成:
y = FunctionWithYield()
for x in y: print(x)
y = FunctionWithYield()
for x in y: print(x)
Run Code Online (Sandbox Code Playgroud)将生成器结果存储在内存或磁盘上的数据结构中,您可以再次迭代:
y = list(FunctionWithYield())
for x in y: print(x)
# can iterate again:
for x in y: print(x)
Run Code Online (Sandbox Code Playgroud)选项1的缺点是它再次计算值.如果那是CPU密集型的,你最终会计算两次.另一方面,2的缺点是存储.整个值列表将存储在内存中.如果值太多,那可能是不切实际的.
因此,您拥有经典的内存与处理权衡.我无法想象一种在没有存储值或再次计算它们的情况下重绕发生器的方法.
Ant*_*sma 107
另一种选择是使用该itertools.tee()
函数创建生成器的第二个版本:
y = FunctionWithYield()
y, y_backup = tee(y)
for x in y:
print(x)
for x in y_backup:
print(x)
Run Code Online (Sandbox Code Playgroud)
如果原始迭代可能不处理所有项目,则从内存使用的角度来看这可能是有益的.
小智 31
>>> 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
Run Code Online (Sandbox Code Playgroud)
Aar*_*lla 28
可能最简单的解决方案是将昂贵的部件包装在一个对象中并将其传递给生成器:
data = ExpensiveSetup()
for x in FunctionWithYield(data): pass
for x in FunctionWithYield(data): pass
Run Code Online (Sandbox Code Playgroud)
这样,您可以缓存昂贵的计算.
如果您可以同时将所有结果保存在RAM中,则使用list()
在普通列表中实现生成器的结果并使用它.
mic*_*den 19
我想为一个老问题提供不同的解决方案
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)
Run Code Online (Sandbox Code Playgroud)
相比于像这样做的好处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)
Run Code Online (Sandbox Code Playgroud)
如果GrzegorzOledzki的回答不够,你可能会send()
用来完成你的目标.有关增强型生成器和yield表达式的更多详细信息,请参阅PEP-0342.
更新:另见itertools.tee()
.它涉及上面提到的一些内存与处理权衡,但它可以节省一些内存,而不仅仅是将生成器结果存储在list
; 这取决于你如何使用发电机.
如果您的生成器是纯粹的,从某种意义上说,它的输出仅取决于传递的参数和步骤号,并且您希望生成的生成器可重新启动,那么下面的排序片段可能会很方便:
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))
Run Code Online (Sandbox Code Playgroud)
输出:
[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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
正如您在上面看到的,当我们的包装函数捕获StopIteration
异常时,它只是重新初始化生成器对象(使用函数调用的另一个实例)。
然后,假设您在如下某处定义了生成器提供函数,您可以使用 Python 函数装饰器语法隐式包装它:
@generator_wrapper
def generator_generating_function(**kwargs):
for item in ["a value", "another value"]
yield item
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
62497 次 |
最近记录: |