Python生成器可以非惰性地调用吗?

2024-09-30 16:40:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道在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

只有在调用第一个next(g)之后,才对行print('this was evaluated now 1')求值。你知道吗

我想知道是否有一种简单的方法可以不延迟地调用生成器。这意味着在调用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语句之前。这个例子让我意识到,可能无法执行我正在寻找的任务,因为它需要在函数返回后执行步骤(可能需要多线程)。你知道吗


Tags: 函数self内容returnvaluedefcurrentthis
2条回答

你可以为它编写一些装饰程序,例如:

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

信贷:这是由@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

相关问题 更多 >