How can I run the initialization code for a generator function immediately, rather than at the first call?

后端 未结 5 850
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-31 07:46

I have a generator function that goes something like this:

def mygenerator():
    next_value = compute_first_value() # Costly operation
    while next_value          


        
相关标签:
5条回答
  • 2020-12-31 08:26

    I suppose you can yield None after that first statement is completed, then in your calling code:

    gen = mygenerator()
    next(gen) # toss the None
    do_something(gen)
    
    0 讨论(0)
  • 2020-12-31 08:28

    You can create a "preprimed" iterator fairly easily by using 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
    
    0 讨论(0)
  • 2020-12-31 08:32

    For my use case I used a modified version of @ncoghlan answer but wrapped in a factory function to decorate the generating function:

    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
    

    Then just decorate the function:

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

    and the first value will be calculated immediately, and the result is transparent.

    0 讨论(0)
  • 2020-12-31 08:44

    I needed something similar. This is what I landed on. Push the generator function into an inner and return it's call.

    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()
    
    0 讨论(0)
  • 2020-12-31 08:45
    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
    
    0 讨论(0)
提交回复
热议问题