Как я могу запустить код инициализации для функции генератора немедленно, а не при первом вызове? - PullRequest
10 голосов
/ 20 апреля 2011

У меня есть функция генератора, которая выглядит примерно так:

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

Я бы хотел, чтобы этап инициализации (до цикла while) выполнялся сразу после вызова функции, а не только при первом использовании генератора. Какой хороший способ сделать это?

Я хочу сделать это, потому что генератор будет работать в отдельном потоке (или процессе, или любом другом многопроцессорном использовании), и я не буду использовать возвращение в течение короткого времени, а инициализация несколько дорогая, поэтому я хотел бы, чтобы он выполнял инициализацию, пока я готовлюсь использовать значения.

Ответы [ 5 ]

14 голосов
/ 20 апреля 2011
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
10 голосов
/ 11 ноября 2014

Мне нужно что-то подобное. Это то, на чем я приземлился. Вставьте функцию генератора во внутреннюю и верните ее вызов.

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()
8 голосов
/ 20 апреля 2011

Вы можете довольно легко создать «предварительно подготовленный» итератор, используя 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
5 голосов
/ 20 апреля 2011

Полагаю, вы можете выдать None после завершения первого оператора, а затем в своем коде вызова:

gen = mygenerator()
next(gen) # toss the None
do_something(gen)
0 голосов
/ 19 апреля 2016

Для моего случая использования я использовал модифицированную версию @ ncoghlan answer , но обернутую в заводскую функцию для украшения генерирующей функции:

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

, и первое значение будет вычислено немедленно, а результат будет прозрачным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...