2010-10-03 13 views
7

मेरे पास कई पायथन जनरेटर हैं, जिन्हें मैं एक नए जनरेटर में जोड़ना चाहता हूं। मैं yield कथन के समूह का उपयोग करके हाथ से लिखित जनरेटर द्वारा आसानी से ऐसा कर सकता हूं।itertools या हाथ से लिखित जनरेटर - क्या बेहतर है?

दूसरी तरफ, itertools मॉड्यूल इस तरह की चीजों के लिए बनाया गया है और मेरे लिए ऐसा लगता है कि जनरेटर बनाने के लिए पाइथोनिक तरीका मुझे itertools मॉड्यूल के विभिन्न पुनरावृत्तियों को एक साथ प्लग करना है।

हालांकि, समस्या में हाथ में, यह जल्द ही काफी जटिल हो जाता है (जेनरेटर को एक तरह का राज्य बनाए रखने की आवश्यकता होती है --- उदाहरण के लिए कि पहले या बाद के आइटम संसाधित किए जा रहे हैं ---, i-th आउटपुट आगे i-th इनपुट आइटम्स की स्थितियों पर निर्भर करता है और विभिन्न इनपुट सूचियों को जेनरेटेड सूची में शामिल होने से पहले अलग-अलग संसाधित किया जाना चाहिए।

मानक समस्याकर्ताओं की संरचना के रूप में जो मेरी समस्या का समाधान करेगी --- देय स्रोत कोड लिखने की एक-आयामी प्रकृति के लिए --- लगभग समझ में नहीं आता है, मुझे आश्चर्य है कि मानक itertools जेनरेटर बनाम हाथ से लिखित जेनरेटर फ़ंक्शंस (मूल और अधिक उन्नत मामलों में) का उपयोग करने के कोई फायदे हैं। असल में, मुझे लगता है कि 90% मामलों में, एच और लिखित संस्करणों को पढ़ने के लिए बहुत आसान है --- संभवतः चेनिंग इटरेटर्स की कार्यात्मक शैली की तुलना में उनकी अधिक अनिवार्य शैली के कारण। a और b एक ही लंबाई (इनपुट डेटा) के दो iterables हो:

संपादित

आदेश मेरी समस्या को वर्णन करने में, यहाँ एक (खिलौना) उदाहरण है। a के आइटम पूर्णांक से युक्त हैं, b के आइटम खुद को पुनरावृत्त कर रहे हैं, जिनके व्यक्तिगत आइटम तार हैं।

from itertools import * 
def generator2(a, b): 
    return (z for i, s, c in izip(a, b, count()) 
      for y in (("First line" if c == 0 else "Some later line",), 
         ("The parameter vanishes.",) if i == 0 
         else ("The parameter is:", i), 
         ("The strings are:",), 
         islice((x for t in s for x in (',', t)), 1, None)) 
      for z in y) 
:

from itertools import * 
def generator(a, b): 
    first = True 
    for i, s in izip(a, b): 
     if first: 
      yield "First line" 
      first = False 
     else: 
      yield "Some later line" 
     if i == 0: 
      yield "The parameter vanishes." 
     else: 
      yield "The parameter is:" 
      yield i 
     yield "The strings are:" 
     comma = False 
     for t in s: 
      if comma: 
       yield ',' 
      else: 
       comma = True 
      yield t 

अगर मैं जनरेटर भाव और itertools मॉड्यूल का उपयोग कर कार्यात्मक शैली में एक ही कार्यक्रम लिख, मैं की तरह कुछ के साथ अंत: उत्पादन निम्नलिखित जनरेटर फ़ंक्शन के परिणाम के अनुरूप होना चाहिए

उदाहरण

>>> a = (1, 0, 2), ("ab", "cd", "ef") 
>>> print([x for x in generator(a, b)]) 
['First line', 'The parameter is:', 1, 'The strings are:', 'a', ',', 'b', 'Some later line', 'The parameter vanishes.', 'The strings are:', 'c', ',', 'd', 'Some later line', 'The parameter is:', 2, 'The strings are:', 'e', ',', 'f'] 
>>> print([x for x in generator2(a, b)]) 
['First line', 'The parameter is:', 1, 'The strings are:', 'a', ',', 'b', 'Some later line', 'The parameter vanishes.', 'The strings are:', 'c', ',', 'd', 'Some later line', 'The parameter is:', 2, 'The strings are:', 'e', ',', 'f'] 

यह poss है मेरे पहले समाधान की तुलना में अधिक सुरुचिपूर्ण लेकिन यह एक बार-लिखने-समझने-बाद-समझने वाले कोड के टुकड़े जैसा दिखता है। मैं सोच रहा हूं कि मेरे जेनरेटर लिखने के इस तरीके के पास पर्याप्त फायदे हैं जो किसी को ऐसा करना चाहिए।

पीएस: मुझे लगता है कि कार्यात्मक समाधान के साथ मेरी समस्या का हिस्सा यह है कि पाइथन में कीवर्ड की मात्रा को कम करने के लिए, "के लिए", "if" और "else" जैसे कुछ कीवर्ड अभिव्यक्तियों में उपयोग के लिए पुनर्नवीनीकरण किए गए हैं ताकि अभिव्यक्ति में उनकी नियुक्ति का उपयोग किया जा सके (जेनरेटर अभिव्यक्ति z for x in a for y in x for z in y में क्रमशः क्लासिक for लूप: for x in a: for y in x: for z in y: yield z में ऑर्डर करने से कम प्राकृतिक, कम से कम मेरे लिए दिखता है)।

+0

पुनश्च: मैं सिर्फ बाहर पाया है मैं जनरेटर समारोह से चिपक है। यह एक काम कर सकता है, मैं जनरेटर अभिव्यक्तियों के साथ नहीं कर सकता, अर्थात् अपवादों को पकड़ना। – Marc

उत्तर

7

मैंने कुछ प्रोफाइलिंग किया और नियमित जनरेटर फ़ंक्शन आपके दूसरे जनरेटर या मेरे कार्यान्वयन से कहीं अधिक तेज है।

$ python -mtimeit -s'import gen; a, b = gen.make_test_case()' 'list(gen.generator1(a, b))' 
10 loops, best of 3: 169 msec per loop 

$ python -mtimeit -s'import gen; a, b = gen.make_test_case()' 'list(gen.generator2(a, b))' 
10 loops, best of 3: 489 msec per loop 

$ python -mtimeit -s'import gen; a, b = gen.make_test_case()' 'list(gen.generator3(a, b))' 
10 loops, best of 3: 385 msec per loop 

यह भी सबसे अधिक पढ़ने योग्य होता है इसलिए मुझे लगता है कि मैं इसके साथ जाऊंगा।ऐसा कहा जा रहा है कि, मैं अभी भी अपना समाधान पोस्ट करूंगा क्योंकि मुझे लगता है कि यह कार्यात्मक प्रोग्रामिंग के प्रकार का एक क्लीनर उदाहरण है जिसे आप itertools के साथ कर सकते हैं (हालांकि स्पष्ट रूप से अभी भी इष्टतम नहीं है, मुझे लगता है कि यह नियमित जेनरेटर फ़ंक्शन को धुआं करने में सक्षम होना चाहिए। मैं इस पर हैक जाएगा)

def generator3(parameters, strings): 
    # replace strings with a generator of generators for the individual charachters 
    strings = (it.islice((char for string_char in string_ for char in (',', string_char)), 1, None) 
       for string_ in strings) 

    # interpolate strings with the notices 
    strings = (it.chain(('The strings are:',), string_) for string_ in strings) 

    # nest them in tuples so they're ate the same level as the other generators 
    separators = it.chain((('First line',),), it.cycle((('Some later line',),))) 

    # replace the parameters with the appropriate tuples 
    parameters = (('The parameter is:', p) if p else ('The parameter vanishes.',) 
        for p in parameters) 

    # combine the separators, parameters and strings 
    output = it.izip(separators, parameters, strings) 

    # flatten it twice and return it 
    output = it.chain.from_iterable(output) 
    return it.chain.from_iterable(output) 
संदर्भ के लिए

, परीक्षण का मामला है:

def make_test_case(): 
    a = [i % 100 for i in range(10000)] 
    b = [('12345'*10)[:(i%50)+1] for i in range(10000)] 
    return a, b 
+0

उपरोक्त EDIT अनुभाग के तहत कुछ कोड पोस्ट किया है। – Marc

+0

@ मर्क, आपने अभी भी असली भ्रम को ठीक नहीं किया है, चाहे आप चाहते हैं कि "पैरामीटर है:" और 'i' अलग से या एक ट्यूपल के रूप में उत्पन्न हुआ। – aaronasterling

+0

कोड के पहले भाग को सही किया गया (मुझे भ्रम के लिए खेद है, लेकिन मैंने जल्दी जेनरेटर फ़ंक्शन को जल्दबाजी में टाइप किया जिसने मुझे इसके बजाय लिखने के लिए लिखा।) "उपज x; उपज वाई" और "(" एक्स, वाई) ": आउटपुट को लूप के लिए दूसरे द्वारा उपभोग किया जाना है, इसलिए मेरे उद्देश्यों के लिए, यह कोई फर्क नहीं पड़ता कि आउटपुट" (x, y) "या" iter ((x, y)) जैसा है " । हालांकि ऊपर दिए गए मेरे उदाहरण में, फ़ंक्शन जनरेटर 2 जनरेटर आउटपुट करता है, जो फ़ंक्शन जनरेटर के आउटपुट की तरह व्यवहार करता है। मुझे नहीं लगता कि जनरेटर 2 आउटपुट tuples (मैंने अपने सिस्टम पर कोड चलाया है।) – Marc

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