14

我有一个生成器函数,它是这样的:

def mygenerator():
    next_value = compute_first_value() # Costly operation
    while next_value != terminating_value:
        yield next_value
        next_value = compute_next_value()

我希望在调用函数后立即运行初始化步骤(在 while 循环之前),而不是仅在第一次使用生成器时运行。有什么好方法可以做到这一点?

我想这样做是因为生成器将在单独的线程(或进程,或任何多处理使用)中运行,并且我不会在短时间内使用返回,并且初始化有点昂贵,所以我想要它在我准备使用这些值时进行初始化。

4

5 回答 5

14
class mygenerator(object):
    def __init__(self):
        next_value = compute_first_value()
    def __iter__(self):
        return self
    def next(self):
        if next_value == terminating_value:
            raise StopIteration()
        return next_value
于 2011-04-20T00:56:24.593 回答
14

我需要类似的东西。这就是我登陆的地方。将生成器函数推入内部并返回它的调用。

def mygenerator():
    next_value = compute_first_value()

    def generator():
        while next_value != terminating_value:
            yield next_value
            next_value = compute_next(next_value)

    return generator()
于 2014-11-11T20:05:50.223 回答
9

您可以使用以下方法相当容易地创建一个“预先准备好的”迭代器itertools.chain

from itertools import chain

def primed(iterable):
    """Preprimes an iterator so the first value is calculated immediately
       but not returned until the first iteration
    """
    itr = iter(iterable)
    try:
        first = next(itr)  # itr.next() in Python 2
    except StopIteration:
        return itr
    return chain([first], itr)

>>> def g():
...     for i in range(5):
...         print("Next called")
...         yield i
...
>>> x = primed(g())
Next called
>>> for i in x: print(i)
...
0
Next called
1
Next called
2
Next called
3
Next called
4
于 2011-04-20T02:58:38.513 回答
5

我想你可以在第一条语句完成后产生 None ,然后在你的调用代码中:

gen = mygenerator()
next(gen) # toss the None
do_something(gen)
于 2011-04-19T23:47:29.443 回答
0

对于我的用例,我使用了@ncoghlan 答案的修改版本,但包装在工厂函数中来装饰生成函数:

import collections, functools, itertools

def primed_generator(generating_function):
    @functools.wraps(generating_function)
    def get_first_right_away_wrapper(*args,**kw):
        "call the generator function, prime before returning"
        gen = generating_function(*args,**kw)
        assert isinstance(gen,collections.Iterator)
        first_value = next(gen)
        return itertools.chain((first_value,),gen)
    return get_first_right_away_wrapper

然后只是装饰功能:

@primed_generator
def mygenerator():
    next_value = compute_first_value() # Costly operation
    while next_value != terminating_value:
        yield next_value
        next_value = compute_next_value()

并且会立即计算第一个值,结果是透明的。

于 2016-04-19T19:00:32.913 回答