2011-04-19 6 views
8

Şunun gibi bir işleve sahip bir jeneratör fonksiyonuna sahibim:İlk çağrıdansa, bir jeneratör işlevi için başlatma kodunu nasıl hemen çalıştırabilirim?

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

Sadece jeneratör ilk kullanıldığında değil, işlev çağrılır başlatılmaz başlatması adımını (while döngüsünden önce) çalıştırmak istiyorum . Bunu yapmanın iyi bir yolu nedir?

Bunu yapmak istiyorum çünkü jeneratör ayrı bir iş parçacığıyla (veya işlem ne olursa olsun veya çoklu işlem ne olursa olsun) çalışacak ve kısa bir süre için geri dönüşü kullanmayacak ve başlatma biraz maliyetli olacak. Değerleri kullanmaya hazırlanırken başlatmayı yapmak isterim.

cevap

13
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 
+1

Bu "doğru" yoldur. Diğerleri hack. Yazmak ve kullanmak, yazmak için daha kısa, ama okumak zor ve akılda tutmak zordur. – jsbueno

6

Ben senin çağıran kod sonra, bu ilk ifadesi tamamlandıktan sonra Hiçbiri verebilmesidir varsayalım:

gen = mygenerator() 
next(gen) # toss the None 
do_something(gen) 
7

yapabilirsiniz itertools.chain kullanarak bir "preprimed" yineleyici oldukça kolay oluşturun:

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 
6

Benzer bir şeye ihtiyacım vardı. Bu benim indiğim şey. Jeneratör işlevini bir iç kısma doğru itin ve bu çağrıya geri dönün.

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 

Sonra sadece işlev süslemeleri:

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

kullanımım durum için

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

Ben üreten fonksiyonunu süslemek için @ncoghlan answer değiştirilmiş bir sürümü kullanılan ancak bir fabrika işlevinde sarılmış ve ilk değer hemen hesaplanacak ve sonuç şeffaf.

İlgili konular