एक फ़ंक्शन से रिटर्न या उपज जो एक जनरेटर को बुलाता है?


30

मैं एक जनरेटर है generatorऔर यह भी यह करने के लिए एक सुविधा की विधि - generate_all

def generator(some_list):
  for i in some_list:
    yield do_something(i)

def generate_all():
  some_list = get_the_list()
  return generator(some_list) # <-- Is this supposed to be return or yield?

चाहिए generate_all returnया नहीं yield? मैं चाहता हूं कि दोनों विधियों के उपयोगकर्ता इसे एक ही उपयोग करें, अर्थात

for x in generate_all()

के बराबर होना चाहिए

some_list = get_the_list()
for x in generate(some_list)

2
या तो उपयोग करने का एक कारण है। इस उदाहरण के लिए, रिटर्न अधिक कुशल है
मैड फिजिसिस्ट

1
यह मुझे एक समान प्रश्न की याद दिलाता है जिसे मैंने एक बार पेश किया था: "पुनरावृति से उपज" बनाम "वापसी की पुनरावृत्ति (चलने योग्य)" । जबकि विशेष रूप से जनरेटर के बारे में नहीं यह मूल रूप से जनरेटर के रूप में ही है और पुनरावृत्तियों अजगर में काफी समान हैं। साथ ही उत्तर द्वारा प्रस्तावित बायोटेक की तुलना करने की रणनीति यहां उपयोग की जा सकती है।
पीटरई

जवाबों:


12

जब आप अपने कोड को डीबग कर रहे हों या यदि कोई अपवाद फेंकता है, तो जेनरेटर आलसी-मूल्यांकन कर रहे हैं returnया yieldअलग तरह से व्यवहार करेंगे।

returnआपके अपवाद में होने वाले किसी भी अपवाद के generatorबारे में कुछ भी नहीं पता होगा generate_all, ऐसा इसलिए है क्योंकि जब generatorवास्तव में निष्पादित किया जाता है तो आप पहले ही generate_allफ़ंक्शन छोड़ चुके हैं । yieldवहाँ के साथ यह generate_allट्रेसबैक में होगा ।

def generator(some_list):
    for i in some_list:
        raise Exception('exception happened :-)')
        yield i

def generate_all():
    some_list = [1,2,3]
    return generator(some_list)

for item in generate_all():
    ...
Exception                                 Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
      8     return generator(some_list)
      9 
---> 10 for item in generate_all():
     11     ...

<ipython-input-3-b19085eab3e1> in generator(some_list)
      1 def generator(some_list):
      2     for i in some_list:
----> 3         raise Exception('exception happened :-)')
      4         yield i
      5 

Exception: exception happened :-)

और अगर यह उपयोग कर रहा है yield from:

def generate_all():
    some_list = [1,2,3]
    yield from generator(some_list)

for item in generate_all():
    ...
Exception                                 Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
      8     yield from generator(some_list)
      9 
---> 10 for item in generate_all():
     11     ...

<ipython-input-4-be322887df35> in generate_all()
      6 def generate_all():
      7     some_list = [1,2,3]
----> 8     yield from generator(some_list)
      9 
     10 for item in generate_all():

<ipython-input-4-be322887df35> in generator(some_list)
      1 def generator(some_list):
      2     for i in some_list:
----> 3         raise Exception('exception happened :-)')
      4         yield i
      5 

Exception: exception happened :-)

हालांकि यह प्रदर्शन की कीमत पर आता है। अतिरिक्त जनरेटर परत में कुछ ओवरहेड होता है। तो returnआम तौर पर yield from ...(या for item in ...: yield item) की तुलना में थोड़ा तेज होगा । ज्यादातर मामलों में यह ज्यादा मायने नहीं रखता, क्योंकि जेनरेटर में आप जो भी करते हैं वह आमतौर पर रन-टाइम पर हावी हो जाता है ताकि अतिरिक्त लेयर पर ध्यान न दिया जा सके।

हालांकि yieldइसके कुछ अतिरिक्त लाभ हैं: आप एक भी चलने के लिए सीमित नहीं हैं, आप आसानी से अतिरिक्त आइटम भी प्राप्त कर सकते हैं:

def generator(some_list):
    for i in some_list:
        yield i

def generate_all():
    some_list = [1,2,3]
    yield 'start'
    yield from generator(some_list)
    yield 'end'

for item in generate_all():
    print(item)
start
1
2
3
end

आपके मामले में ऑपरेशन काफी सरल हैं और मुझे नहीं पता कि क्या इसके लिए कई फ़ंक्शन बनाने के लिए भी आवश्यक है, कोई आसानी से mapइसके बजाय अंतर्निहित या एक जनरेटर अभिव्यक्ति का उपयोग कर सकता है:

map(do_something, get_the_list())          # map
(do_something(i) for i in get_the_list())  # generator expression

उपयोग करने के लिए दोनों समान होना चाहिए (कुछ मतभेदों को छोड़कर)। और अगर उन्हें अधिक वर्णनात्मक नाम की आवश्यकता है, तो आप उन्हें अभी भी एक फ़ंक्शन में लपेट सकते हैं।

ऐसे कई हेल्पर्स हैं, जो बिल्ट-इन में मौजूद iterables पर बहुत ही सामान्य ऑपरेशन को लपेटते हैं और आगे के बिल्ट-इन itertoolsमॉड्यूल में पाए जा सकते हैं । ऐसे सरल मामलों में मैं केवल इनका सहारा लेता हूं और केवल गैर-तुच्छ मामलों के लिए अपने स्वयं के जनरेटर लिखते हैं।

लेकिन मुझे लगता है कि आपका वास्तविक कोड अधिक जटिल है, जो लागू नहीं हो सकता है, लेकिन मैंने सोचा कि यह विकल्पों का उल्लेख किए बिना एक पूर्ण उत्तर नहीं होगा।


17

आप शायद जेनरेटर डेलिगेशन (PEP380) की तलाश कर रहे हैं

सरल पुनरावृत्तियों के लिए, yield from iterableअनिवार्य रूप से इसका संक्षिप्त रूप हैfor item in iterable: yield item

def generator(iterable):
  for i in iterable:
    yield do_something(i)

def generate_all():
  yield from generator(get_the_list())

यह बहुत संक्षिप्त है और इसमें कई अन्य फायदे भी हैं, जैसे कि मनमानी / विभिन्न पुनरावृत्तियों को चेन करने में सक्षम होना!


ओह आप के नामकरण का मतलब है list? यह एक बुरा उदाहरण है, प्रश्न में वास्तविक कोड नहीं चिपकाया गया है, मुझे शायद इसे संपादित करना चाहिए।
हाइनाकोव

हाँ - कभी डरो नहीं, मैं उदाहरण कोड का बहुत दोषी हूँ जो पहली बार में भी नहीं चलेगा ..
ti7

2
पहले एक एक लाइनर भी हो सकता है :)। yield from map(do_something, iterable)या यहां तक ​​किyield from (do_something(x) for x in iterable)
मैड फिजिसिस्ट

2
"यह नीचे सभी तरह कोड उदाहरण है!"
ti7

3
आपको केवल प्रतिनिधिमंडल की आवश्यकता है यदि आप हैं, तो आप, नए जनरेटर को वापस करने के अलावा कुछ और कर रहे हैं। यदि आप नया जनरेटर वापस करते हैं, तो किसी प्रतिनिधिमंडल की जरूरत नहीं है। तो yield fromव्यर्थ जब तक आपके आवरण करता है कुछ बाकी जनरेटर-y।
शैडो रेंजर

14

return generator(list)जो तुम चाहते हो। लेकिन ध्यान दें

yield from generator(list)

समतुल्य होगा, लेकिन generatorसमाप्त होने के बाद अधिक मूल्यों के उत्पादन के अवसर के साथ । उदाहरण के लिए:

def generator_all_and_then_some():
    list = get_the_list()
    yield from generator(list)
    yield "one last thing"

5
मेरा मानना है कि वहाँ के बीच एक सूक्ष्म अंतर है yield fromऔर returnजब जनरेटर के उपभोक्ता throwsइसके बारे में एक अपवाद के अंदर - और अन्य कार्यों है कि स्टैक ट्रेस से प्रभावित हैं के साथ।
वर्ल्डसेंडर

9

इस विशेष मामले में निम्नलिखित दो कथन कार्यात्मक रूप से समतुल्य प्रतीत होंगे:

return generator(list)

तथा

yield from generator(list)

बाद में लगभग समान है

for i in generator(list):
    yield i

returnबयान जनरेटर आप देख रहे हैं देता है। एक yield fromया yieldस्टेटमेंट आपके पूरे फंक्शन को एक ऐसी चीज़ में बदल देता है, जो एक जनरेटर को लौटाता है, जो आपके द्वारा खोजे जा रहे हैं।

उपयोगकर्ता के दृष्टिकोण से, कोई अंतर नहीं है। आंतरिक रूप से, हालांकि, returnयकीनन अधिक कुशल है क्योंकि यह generator(list)एक उत्कृष्ट पास-थ्रू जनरेटर में लपेटता नहीं है । यदि आप लिपटे जनरेटर के तत्वों पर कोई प्रसंस्करण करने की योजना बनाते हैं, तो किसी न किसी रूप में उपयोग करें yield


4

आप returnइसे करेंगे ।

yieldआईएनजी * generate_all()एक जनरेटर के लिए खुद का मूल्यांकन करने का कारण होगा , और nextउस बाहरी जनरेटर को कॉल करने से आंतरिक जनरेटर पहले फ़ंक्शन द्वारा वापस आ जाएगा, जो कि वह नहीं चाहेगा।

* शामिल नहीं yield from

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.