我知道在Python中,生成器是延迟调用的。例如:
>>> def G():
... print('this was evaluated now 1')
... yield 1
... print('this was evaluated now 2')
... yield 2
...
>>> g = G()
>>> next(g)
this was evaluated now 1
1
>>> next(g)
this was evaluated now 2
2
仅在调用第一个print('this was evaluated now 1')
之后才评估行next(g)
。
我想知道是否有一种简单的方法可以懒惰地调用生成器。这意味着,在调用g = G()
时,该函数将计算直至并包括第一个yield
结果的所有内容,而不会实际产生。然后,在第一次调用next(g)
时,将产生已经计算的结果,并且还将计算直到第二个yield
结果的所有内容。依此类推。
如何实现?
这是此非懒惰方案下的预期行为:
>>> g = G()
this was evaluated now 1
>>> next(g)
1
this was evaluated now 2
>>> next(g)
2
这是解决方案尝试,不起作用:
>>> class NonLazyGenerator():
... def __init__(self,G):
... self.g = G()
... self.next_value = next(self.g)
...
... def __next__(self):
... current_value = self.next_value
... try:
... self.next_value = next(self.g)
... except StopIteration:
... pass
... return current_value
...
>>> g = NonLazyGenerator(G)
this was evaluated now 1
>>> next(g)
this was evaluated now 2
1
>>> next(g)
2
这失败了,因为仅在return
语句之后才产生值,而直到下一个yield
的所有内容的计算都在return
语句之前进行。这个示例使我意识到,可能无法执行我要寻找的内容,因为它需要在函数返回后执行一些步骤(可能需要多线程处理)。
答案 0 :(得分:1)
您可能会为此编写某种装饰器,例如:
def eagergenerator(mygen):
class GeneratorWrapper:
def __init__(self, *args, **kwargs):
self.g = mygen(*args, **kwargs)
self.last = next(self.g)
def __iter__(self):
return self
def __next__(self):
if self.last is self:
raise StopIteration
fake_yield = self.last
try:
self.last = next(self.g)
return fake_yield
except StopIteration:
self.last = self
return fake_yield
return GeneratorWrapper
然后,您可以简单地装饰普通的发电机:
@eagergenerator
def G():
print("one")
yield 1
print("two")
yield 2
其工作方式如下:
>>> g = G()
one
>>> next(g)
two
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "eagergen.py", line 10, in __next__
raise StopIteration
StopIteration
>>>
答案 1 :(得分:1)
信用:这是受@ L3viathan答案的启发
在此版本中,itertools.tee用于存储包装器在原始生成器后面的一个屈服值。
import itertools
def eagergenerator(mygen):
class GeneratorWrapper:
def __init__(self, *args, **kwargs):
self.g0, self.g1 = itertools.tee(mygen(*args, **kwargs))
self._next0()
def _next0(self):
try:
next(self.g0)
except StopIteration:
pass
def __iter__(self):
return self
def __next__(self):
self._next0()
return next(self.g1)
return GeneratorWrapper