2011-04-19 12 views
8

मैं एक जनरेटर समारोह है कि कुछ इस तरह चला जाता है है: के बजाय जैसे ही समारोह के रूप में कहा जाता है को चलाने के लिएमैं पहले कॉल के बजाए जेनरेटर फ़ंक्शन के लिए प्रारंभिक कोड कैसे चला सकता हूं?

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

मैं (जबकि पाश से पहले) प्रारंभ कदम चाहते हैं केवल जब जनरेटर पहला है उपयोग किया गया। ऐसा करने का एक अच्छा तरीका क्या है?

मैं ऐसा करना चाहता हूं क्योंकि जनरेटर एक अलग थ्रेड (या प्रक्रिया, या जो भी मल्टीप्रोसेसिंग उपयोग करता है) में चल रहा है और मैं थोड़ी देर के लिए वापसी का उपयोग नहीं करूँगा, और प्रारंभिक कुछ महंगा है, इसलिए मैं इसे प्रारंभिक करना चाहता हूं जबकि मैं मूल्यों का उपयोग करने के लिए तैयार हो रहा हूं।

उत्तर

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

यह "सही" तरीका है। अन्य हैक हैं। वे लिखने के लिए कम, बनाने और उपयोग करने के लिए अपने आप पर मजेदार हो सकते हैं, लेकिन बनाए रखने के लिए दबाने और दिमाग को ध्यान में रखना मुश्किल है। – jsbueno

6

मैं अपने कॉलिंग कोड में तो आप कोई भी प्राप्त हो सकते हैं कि पहले बयान पूरा होने के बाद लगता है,:

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

आप कर सकते हैं एक "preprimed" इटरेटर 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 
6

मुझे कुछ इसी तरह की आवश्यकता थी। यही वह है जो मैंने उतरा। जेनरेटर फ़ंक्शन को एक भीतरी में दबाएं और उसे कॉल करें।

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

मेरी उपयोग के मामले के लिए मैं @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() 

और पहले मान की गणना तुरंत की जाएगी, और परिणाम पारदर्शी है।

संबंधित मुद्दे