"उपज" कीवर्ड क्या करता है?


10183

yieldपायथन में कीवर्ड का उपयोग क्या है , और यह क्या करता है?

उदाहरण के लिए, मैं इस कोड को समझने की कोशिश कर रहा हूं 1 :

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

और यह फोन करने वाला है:

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

क्या होता है जब विधि _get_child_candidatesकहा जाता है? क्या कोई सूची लौटा दी गई है? एक एकल तत्व? क्या इसे फिर से बुलाया जाता है? बाद में कॉल कब बंद होंगी?


1. कोड का यह टुकड़ा जोचेन शुल्ज़ (jrschulz) द्वारा लिखा गया था, जिन्होंने मेट्रो रिक्त स्थान के लिए एक महान पायथन लाइब्रेरी बनाई थी। यह पूर्ण स्रोत का लिंक है: मॉड्यूल क्षेत्र

जवाबों:


14637

यह समझने के लिए कि yieldआपको क्या समझना चाहिए, जनरेटर क्या हैं। और इससे पहले कि आप जनरेटर को समझ सकें, आपको पुनरावृत्तियों को समझना चाहिए ।

Iterables

जब आप एक सूची बनाते हैं, तो आप एक-एक करके इसके आइटम पढ़ सकते हैं। इसकी वस्तुओं को एक-एक करके पढ़ना पुनरावृत्ति कहलाता है:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylistएक चलने योग्य है । जब आप किसी सूची की समझ का उपयोग करते हैं, तो आप एक सूची बनाते हैं, और इसलिए एक पुनरावृत्ति:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

आप for... in...जिस चीज़ पर " " का उपयोग कर सकते हैं वह एक पुनरावृति है; lists, stringsफाइलें, ...

ये पुनरावृत्तियां आसान हैं क्योंकि आप उन्हें अपनी इच्छानुसार पढ़ सकते हैं, लेकिन आप सभी मानों को स्मृति में संग्रहीत करते हैं और यह हमेशा वैसा नहीं होता है जब आप बहुत सारे मूल्यों को चाहते हैं।

जेनरेटर

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

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

यह सिर्फ एक ही है सिवाय इसके कि आप के ()बजाय इस्तेमाल किया []। लेकिन, आप दूसरी बार प्रदर्शन नहीं कर सकतेfor i in mygenerator क्योंकि जनरेटर का उपयोग केवल एक बार किया जा सकता है: वे 0 की गणना करते हैं, फिर इसके बारे में भूल जाते हैं और 1 की गणना करते हैं, और 4 की गणना करते हैं, एक-एक करके।

प्राप्ति

yieldएक कीवर्ड है जिसका उपयोग किया जाता है return, जैसे फ़ंक्शन को छोड़कर एक जनरेटर लौटाएगा।

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

यहां यह एक बेकार उदाहरण है, लेकिन यह तब उपयोगी है जब आप जानते हैं कि आपका फ़ंक्शन मानों का एक बड़ा सेट लौटाएगा, जिसे आपको केवल एक बार पढ़ना होगा।

मास्टर करने के लिए yield, आपको यह समझना चाहिए कि जब आप फ़ंक्शन को कॉल करते हैं, तो फ़ंक्शन बॉडी में आपके द्वारा लिखा गया कोड नहीं चलता है। फ़ंक्शन केवल जनरेटर ऑब्जेक्ट लौटाता है, यह थोड़ा मुश्किल है :-)

फिर, आपका कोड जारी रहेगा जहां से यह रवाना होता है कि हर बार forजनरेटर का उपयोग होता है।

अब मुश्किल हिस्सा:

पहली बार forआपके फ़ंक्शन से निर्मित जनरेटर ऑब्जेक्ट को कॉल करता है, यह आपके फ़ंक्शन में कोड को शुरुआत से चलाएगा जब तक कि यह हिट नहीं हो जाता है yield, तब यह लूप के पहले मूल्य को वापस कर देगा। फिर, प्रत्येक बाद की कॉल फ़ंक्शन में लिखे गए लूप का एक और चलना चलाएगी और अगला मान लौटाएगी। यह तब तक जारी रहेगा जब तक कि जनरेटर को खाली नहीं माना जाता है, जो तब होता है जब फ़ंक्शन बिना छेद के चलता है yield। ऐसा इसलिए हो सकता है क्योंकि लूप समाप्त हो गया है, या क्योंकि आप अब संतुष्ट नहीं हैं "if/else"


आपका कोड समझाया गया

जनरेटर:

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if the distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if the distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there is no more than two values: the left and the right children

कॉलर:

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidate's list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

इस कोड में कई स्मार्ट भाग हैं:

  • एक सूची पर लूप पुनरावृत्त होता है, लेकिन सूची का विस्तार होता है जबकि लूप को पुनरावृत्त किया जा रहा है :-) यह इन सभी नेस्टेड डेटा के माध्यम से जाने का एक संक्षिप्त तरीका है, भले ही यह थोड़ा खतरनाक हो क्योंकि आप अनंत लूप के साथ समाप्त हो सकते हैं। इस स्थिति में, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))जनरेटर के सभी मूल्यों को समाप्त कर देता है, लेकिन whileनए जनरेटर ऑब्जेक्ट बनाता रहता है जो पिछले वाले से अलग-अलग मान उत्पन्न करेगा क्योंकि यह एक ही नोड पर लागू नहीं होता है।

  • extend()विधि एक सूची वस्तु विधि है कि एक iterable अपेक्षा करता है और सूची में अपने मूल्यों को जोड़ता है।

आमतौर पर हम इसे एक सूची देते हैं:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

लेकिन आपके कोड में, यह एक जनरेटर है, जो अच्छा है क्योंकि:

  1. आपको दो बार मान पढ़ने की आवश्यकता नहीं है।
  2. आपके पास बहुत सारे बच्चे हो सकते हैं और आप नहीं चाहते कि वे सभी मेमोरी में संग्रहीत हों।

और यह काम करता है क्योंकि पायथन को परवाह नहीं है कि क्या विधि का तर्क एक सूची है या नहीं। अजगर उम्मीद करता है कि यह तार, सूची, ट्यूपल्स और जनरेटर के साथ काम करेगा! इसे डक टाइपिंग कहा जाता है और यही एक कारण है कि पायथन इतना अच्छा है। लेकिन यह एक और कहानी है, एक और सवाल के लिए ...

आप यहां रुक सकते हैं, या जनरेटर का उन्नत उपयोग देखने के लिए थोड़ा सा पढ़ सकते हैं:

एक जनरेटर थकावट को नियंत्रित करना

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

नोट: पायथन 3 के लिए, का उपयोग करें print(corner_street_atm.__next__())याprint(next(corner_street_atm))

यह विभिन्न चीजों के लिए उपयोगी हो सकता है जैसे किसी संसाधन तक पहुंच को नियंत्रित करना।

Itertools, तुम्हारा सबसे अच्छा दोस्त

Itertools मॉड्यूल में पुनरावृत्तियों को हेरफेर करने के लिए विशेष कार्य शामिल हैं। कभी एक जनरेटर की नकल करना चाहते हैं? चेन दो जनरेटर? वन-लाइनर के साथ नेस्टेड सूची में समूह मान? Map / Zipदूसरी सूची बनाए बिना?

फिर बस import itertools

एक उदाहरण? आइए देखें चार-घोड़े की दौड़ के लिए आने के संभावित आदेश:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

पुनरावृति के आंतरिक तंत्र को समझना

Iteration iterables ( __iter__()पद्धति को लागू करने) और iterators (विधि को लागू करने) को लागू करने वाली एक प्रक्रिया है __next__()। Iterables किसी भी ऑब्जेक्ट हैं जिनसे आप एक पुनरावृत्ति प्राप्त कर सकते हैं। Iterators ऑब्जेक्ट हैं जो आपको पुनरावृत्तियों पर पुनरावृति करने देते हैं।

इस लेख में इसके बारे में अधिक है कि forलूप कैसे काम करते हैं


353
yieldयह उत्तर उतना जादुई नहीं है जितना कि यह उत्तर बताता है। जब आप एक फ़ंक्शन कहते हैं जिसमें yieldकहीं भी एक बयान होता है , तो आपको एक जनरेटर ऑब्जेक्ट मिलता है, लेकिन कोई कोड नहीं चलता है। फिर हर बार जब आप जनरेटर से एक ऑब्जेक्ट निकालते हैं, तो पायथन फ़ंक्शन में कोड निष्पादित करता है जब तक कि यह एक yieldबयान नहीं आता है , तब ऑब्जेक्ट को रोक देता है और वितरित करता है। जब आप किसी अन्य ऑब्जेक्ट को निकालते हैं, तो पायथन बस के बाद फिर से शुरू होता है yieldऔर तब तक जारी रहता है जब तक कि यह दूसरे तक नहीं पहुंचता yield(अक्सर एक ही, लेकिन बाद में एक पुनरावृत्ति)। यह तब तक जारी रहता है जब तक कि फ़ंक्शन अंत तक नहीं चलता है, जिस बिंदु पर जनरेटर को समाप्त माना जाता है।
मथायस फ्रैप

28
"ये पुनरावृत्तियां आसान हैं ... लेकिन आप स्मृति में सभी मूल्यों को संग्रहीत करते हैं और यह हमेशा वह नहीं है जो आप चाहते हैं", या तो गलत है या भ्रमित है। एक चलने-फिरने वाले को iterable पर iter () बुलाने पर एक पुनरावर्तक लौटाता है, और एक पुनरावृत्तिकर्ता को हमेशा अपने मानों को मेमोरी में स्टोर करने की आवश्यकता नहीं होती है, iter पद्धति के कार्यान्वयन के आधार पर , यह मांग पर अनुक्रम में मान भी उत्पन्न कर सकता है।
पिकमेट pic

इस महान उत्तर को जोड़ना अच्छा होगा क्यों आप के ()बजाय इसका उपयोग करने के अलावा सिर्फ एक ही है[] , विशेष रूप से क्या ()है (एक उलझन के साथ भ्रम हो सकता है)।
WoJ

मैं गलत हो सकता हूं, लेकिन एक जनरेटर एक पुनरावृत्ति नहीं है, एक "जनरेटर" एक पुनरावृत्त है।
एडेरोक्स

2006

समझने के लिए शॉर्टकट yield

जब आप किसी फ़ंक्शन को yieldस्टेटमेंट्स के साथ देखते हैं , तो समझने के लिए इस आसान ट्रिक को लागू करें:

  1. result = []फ़ंक्शन की शुरुआत में एक पंक्ति डालें ।
  2. प्रत्येक के yield exprसाथ बदलें result.append(expr)
  3. return resultफ़ंक्शन के नीचे एक पंक्ति डालें ।
  4. याय - कोई और yieldबयान नहीं! कोड पढ़ें और उसका पता लगाएं।
  5. फ़ंक्शन की मूल परिभाषा से तुलना करें।

यह ट्रिक आपको फंक्शन के पीछे के लॉजिक का अंदाजा दे सकती है, लेकिन असल में yieldजो होता है वह लिस्ट बेस्ड एप्रोच में होने वाली तुलना में काफी अलग होता है। कई मामलों में, उपज दृष्टिकोण बहुत अधिक स्मृति कुशल और तेज भी होगा। अन्य मामलों में, यह चाल आपको एक अनंत लूप में फंस जाएगी, भले ही मूल फ़ंक्शन ठीक काम करता हो। अधिक जानकारी के लिए पढ़ें...

अपने Iterables, Iterators और Generators को भ्रमित न करें

सबसे पहले, इटेरेटर प्रोटोकॉल - जब आप लिखते हैं

for x in mylist:
    ...loop body...

पायथन निम्नलिखित दो चरण करता है:

  1. के लिए एक पुनरावृत्ति हो जाता है mylist:

    कॉल iter(mylist)-> यह एक next()विधि के साथ एक वस्तु देता है (या __next__()पायथन 3 में)।

    [यह वह कदम है जिसके बारे में ज्यादातर लोग आपको बताना भूल जाते हैं]

  2. आइटम पर लूप करने के लिए इटरेटर का उपयोग करता है:

    next()चरण 1 से लौटाए गए इट्रेटर पर विधि को कॉल करते रहें । से लौटा मान next()असाइन किया गया है xऔर लूप बॉडी निष्पादित की गई है। यदि कोई अपवाद StopIterationभीतर से उठाया गया है next(), तो इसका मतलब है कि पुनरावृत्त में अधिक मान नहीं हैं और लूप बाहर निकल गया है।

सच्चाई यह है कि पायथन उपरोक्त दो चरणों को करता है कभी भी किसी वस्तु की सामग्री पर लूप करना चाहता है - इसलिए यह लूप के लिए हो सकता है, लेकिन यह कोड की तरह भी हो सकता है otherlist.extend(mylist)(जहां otherlistपायथन सूची है)।

यहाँ mylistएक पुनरावृत्ति है क्योंकि यह पुनरावृत्ति प्रोटोकॉल को लागू करता है। उपयोगकर्ता-परिभाषित वर्ग में, आप __iter__()अपने वर्ग के उदाहरणों को बनाने के लिए विधि लागू कर सकते हैं । इस विधि को एक पुनरावृत्तिकर्ता को लौटाना चाहिए । एक पुनरावृत्त next()विधि के साथ एक वस्तु है । दोनों को एक ही कक्षा में लागू करना __iter__()और वापसी करना संभव है । यह सरल मामलों के लिए काम करेगा, लेकिन तब नहीं जब आप एक ही समय में दो पुनरावृत्तियों को एक ही ऑब्जेक्ट पर लूप करना चाहते हैं।next()__iter__()self

तो यह इटरेटर प्रोटोकॉल है, कई ऑब्जेक्ट इस प्रोटोकॉल को लागू करते हैं:

  1. अंतर्निहित सूचियां, शब्दकोश, ट्यूपल्स, सेट, फाइलें।
  2. उपयोगकर्ता-परिभाषित कक्षाएं जो लागू होती हैं __iter__()
  3. जेनरेटर।

ध्यान दें कि एक forलूप को यह पता नहीं होता है कि वह किस प्रकार की वस्तु से काम कर रहा है - यह इट्रेटर प्रोटोकॉल का अनुसरण करता है, और कॉल करने के बाद आइटम प्राप्त करने के लिए खुश है next()। बिल्ट-इन सूचियाँ एक-एक करके अपने आइटम वापस करती हैं, शब्दकोष एक-एक करके चाबी लौटाते हैं, फाइलें एक-एक करके लाइनें वापस करती हैं , आदि और जेनरेटर वापस लौटते हैं ... अच्छी तरह से वह है जहाँ पर yieldआता है:

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

yieldबयानों के बजाय , यदि आपके पास पहले में तीन returnबयान थे, तो f123()निष्पादित हो जाएगा, और फ़ंक्शन बाहर निकल जाएगा। लेकिन f123()कोई साधारण कार्य नहीं है। जब f123()कहा जाता है, तो यह पैदावार बयानों में किसी भी मूल्य को वापस नहीं करता है ! यह एक जनरेटर वस्तु देता है। इसके अलावा, फ़ंक्शन वास्तव में बाहर नहीं निकलता है - यह एक निलंबित स्थिति में जाता है। जब forलूप जेनरेटर ऑब्जेक्ट पर लूप करने की कोशिश करता है, तो फ़ंक्शन उसके निलंबित स्थिति से फिर से शुरू होने के बाद अगली पंक्ति में फिर से शुरू होता yieldहै, कोड की अगली लाइन को निष्पादित करता है, इस मामले में, एक yieldस्टेटमेंट और अगले के रूप में रिटर्न करता है आइटम। यह तब तक होता है जब तक कि फ़ंक्शन बाहर नहीं निकलता है, जिस बिंदु पर जनरेटर उठता है StopIteration, और लूप बाहर निकलता है।

तो जेनरेटर ऑब्जेक्ट एक एडेप्टर की तरह है - एक छोर पर यह लूप को खुश रखने के तरीकों को उजागर करके __iter__()और next()तरीकों से इट्रेटर प्रोटोकॉल को प्रदर्शित करता है for। हालांकि, दूसरे छोर पर, यह फ़ंक्शन को अगले मूल्य से बाहर निकालने के लिए पर्याप्त चलता है, और इसे वापस निलंबित मोड में डालता है।

क्यों जनरेटर का उपयोग करें?

आमतौर पर, आप कोड लिख सकते हैं जो जनरेटर का उपयोग नहीं करता है लेकिन समान तर्क को लागू करता है। एक विकल्प अस्थायी सूची 'चाल' का उपयोग करना है जिसका मैंने पहले उल्लेख किया था। यह सभी मामलों में काम नहीं करेगा, उदाहरण के लिए यदि आपके पास अनंत लूप हैं, या जब आपके पास वास्तव में लंबी सूची है, तो यह मेमोरी का अक्षम उपयोग कर सकता है। दूसरा तरीका यह है कि एक नए चलने योग्य वर्ग समथिंगआईटर को लागू किया जाए जो राज्य को उदाहरण के सदस्यों में रखता है और इसमें next()(या __next__()पायथन 3 में) विधि में अगला तार्किक चरण निष्पादित करता है । तर्क के आधार पर, next()विधि के अंदर कोड बहुत जटिल लग सकता है और कीड़े के लिए प्रवण हो सकता है। यहां जनरेटर एक साफ और आसान समाधान प्रदान करते हैं।


19
"जब आप पैदावार बयानों के साथ एक समारोह देखते हैं, तो यह समझने के लिए यह आसान चाल लागू करें कि क्या होगा" क्या यह इस तथ्य को पूरी तरह से अनदेखा नहीं करता है कि आप sendजनरेटर में हो सकते हैं , जो जनरेटर के बिंदु का एक बड़ा हिस्सा है?
डेनियलस्क

10
"यह लूप के लिए हो सकता है, लेकिन यह कोड की तरह भी हो सकता है otherlist.extend(mylist)" -> यह गलत है। extend()सूची को जगह में संशोधित करता है और एक पुनरावृत्ति वापस नहीं करता है। पाश की कोशिश कर रहा से अधिक otherlist.extend(mylist)एक साथ विफल हो जाएगा TypeErrorक्योंकि extend()परोक्ष रिटर्न None, और तुम पर पाश नहीं कर सकते हैं None
पेड्रो

4
@pedro आपने उस वाक्य को गलत समझा है। इसका अर्थ है कि निष्पादित करते समय अजगर दो उल्लिखित चरणों mylist(नहीं पर otherlist) करता है otherlist.extend(mylist)
आज 18

555

इस पर इस तरीके से विचार करें:

एक पुनरावृत्ति एक वस्तु के लिए एक फैंसी लगने वाला शब्द है जिसमें एक next()विधि है। तो एक उपज-एड समारोह कुछ इस तरह समाप्त होता है:

मूल संस्करण:

def some_function():
    for i in xrange(4):
        yield i

for i in some_function():
    print i

यह मूल रूप से पायथन दुभाषिया उपरोक्त कोड के साथ क्या करता है:

class it:
    def __init__(self):
        # Start at -1 so that we get 0 when we add 1 below.
        self.count = -1

    # The __iter__ method will be called once by the 'for' loop.
    # The rest of the magic happens on the object returned by this method.
    # In this case it is the object itself.
    def __iter__(self):
        return self

    # The next method will be called repeatedly by the 'for' loop
    # until it raises StopIteration.
    def next(self):
        self.count += 1
        if self.count < 4:
            return self.count
        else:
            # A StopIteration exception is raised
            # to signal that the iterator is done.
            # This is caught implicitly by the 'for' loop.
            raise StopIteration

def some_func():
    return it()

for i in some_func():
    print i

पर्दे के पीछे क्या हो रहा है, इसके बारे में अधिक जानकारी के लिए, forलूप को फिर से लिखा जा सकता है:

iterator = some_func()
try:
    while 1:
        print iterator.next()
except StopIteration:
    pass

क्या यह अधिक समझ में आता है या सिर्फ आपको अधिक भ्रमित करता है? :)

मैं नोट करना चाहिए कि इस है निदर्शी प्रयोजनों के लिए एक अति। :)


1
__getitem__के बजाय परिभाषित किया जा सकता है __iter__। उदाहरण के लिए: class it: pass; it.__getitem__ = lambda self, i: i*10 if i < 10 else [][0]; for i in it(): print(i)यह
छपेगा

16
मैंने पायथन 3.6 में इस उदाहरण की कोशिश की और अगर मैं बनाता हूं iterator = some_function(), तो चर iteratorमें एक फ़ंक्शन नहीं होता है जिसे next()अब कहा जाता है , लेकिन केवल एक __next__()फ़ंक्शन। सोचा था कि इसका जिक्र करूंगा।
पीटर

forआपके द्वारा लिखे गए लूप कार्यान्वयन __iter__को iterator, की तात्कालिक आवृत्ति कहा जाता है it?
सिस्टेमेटिकडिसिनग्रेशन

455

yieldकीवर्ड दो सरल तथ्यों को कम किया जाता है:

  1. यदि कंपाइलर किसी फ़ंक्शन के अंदर कहीं भीyield कीवर्ड का पता लगाता है , तो वह फ़ंक्शन स्टेटमेंट के माध्यम से वापस नहीं आता है । इसके बजाय , यह तुरंत एक आलसी "लंबित सूची" वस्तु देता है जिसे जनरेटर कहा जाता हैreturn
  2. एक जनरेटर चलने योग्य है। एक चलने योग्य क्या है ? यह एक ऐसा कुछ listया setया rangeया dict-व्यू, के साथ एक में निर्मित एक निश्चित क्रम में प्रत्येक तत्व में आने के लिए प्रोटोकॉल

संक्षेप में: एक जनरेटर एक आलसी, वृद्धिशील रूप से लंबित सूची है , और yieldकथन आपको सूची संकेतन का उपयोग करने की अनुमति देते हैं सूची मानों के लिए जनरेटर को आकस्मिक रूप से थूकना चाहिए।

generator = myYieldingFunction(...)
x = list(generator)

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

list==[x[0], x[1], x[2]]

उदाहरण

आइए एक फ़ंक्शन को परिभाषित करें makeRangeजो पायथन की तरह है range। कॉलिंग makeRange(n)एक जनरेटर:

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>

जनरेटर को अपने लंबित मूल्यों को तुरंत वापस करने के लिए मजबूर करने के लिए, आप इसे list()उसी तरह से पारित कर सकते हैं (जैसे आप कोई पुनरावृत्ति कर सकते हैं)

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

"सिर्फ एक सूची लौटाने" के लिए उदाहरण की तुलना करें

उपरोक्त उदाहरण को केवल एक सूची बनाने के बारे में सोचा जा सकता है, जिसे आप वापस करना चाहते हैं:

# list-version                   #  # generator-version
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #~     """return 0,1,2,...,n-1"""
    TO_RETURN = []               #>
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #~         yield i
        i += 1                   #          i += 1  ## indented
    return TO_RETURN             #>

>>> makeRange(5)
[0, 1, 2, 3, 4]

एक प्रमुख अंतर है, हालांकि; अंतिम खंड देखें।


आप जनरेटर का उपयोग कैसे कर सकते हैं

एक पुनरावृत्ति एक सूची बोध का अंतिम हिस्सा है, और सभी जनरेटर चलने योग्य हैं, इसलिए उन्हें अक्सर उपयोग किया जाता है:

#                   _ITERABLE_
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

जनरेटर के लिए बेहतर महसूस करने के लिए, आप itertoolsमॉड्यूल के साथ चारों ओर खेल सकते हैं (वारंट होने पर उपयोग करने के chain.from_iterableबजाय सुनिश्चित करें chain)। उदाहरण के लिए, शायद आप जेनरेटरों का उपयोग असीम रूप से लंबी आलसी सूचियों को लागू करने के लिए भी कर सकते हैं itertools.count()। आप अपना स्वयं का कार्यान्वयन कर सकते हैं def enumerate(iterable): zip(count(), iterable), या वैकल्पिक रूप से yieldथोड़ी देर के लूप में कीवर्ड के साथ कर सकते हैं।

कृपया ध्यान दें: जनरेटर वास्तव में कई और चीजों के लिए उपयोग किया जा सकता है, जैसे कि कोरटाइन या गैर-नियतात्मक प्रोग्रामिंग या अन्य सुरुचिपूर्ण चीजें लागू करना। हालाँकि, मैं यहाँ मौजूद "आलसी सूचियों" का दृष्टिकोण सबसे आम उपयोग है जो आपको मिलेगा।


परदे के पीछे

यह "पायथन पुनरावृत्ति प्रोटोकॉल" कैसे काम करता है। यही है, जब आप करते हैं तो क्या हो रहा है list(makeRange(5))। यह वही है जो मैं पहले "आलसी, वृद्धिशील सूची" के रूप में वर्णित करता हूं।

>>> x=iter(range(5))
>>> next(x)
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

अंतर्निहित फ़ंक्शन next()केवल ऑब्जेक्ट .next()फ़ंक्शन को कॉल करता है, जो "पुनरावृत्ति प्रोटोकॉल" का एक हिस्सा है और सभी पुनरावृत्तियों पर पाया जाता है। आप next()फैंसी चीजों को लागू करने के लिए फ़ंक्शन (और पुनरावृत्ति प्रोटोकॉल के अन्य भागों) को मैन्युअल रूप से उपयोग कर सकते हैं, आमतौर पर पठनीयता की कीमत पर, इसलिए ऐसा करने से बचने की कोशिश करें ...


ज़रा सी बात

आम तौर पर, अधिकांश लोग निम्नलिखित भेदों की परवाह नहीं करते और शायद यहां पढ़ना बंद करना चाहते हैं।

पायथन-स्पीक में, एक पुनरावृत्त कोई भी वस्तु होती है, जो सूची की तरह "फॉर-लूप की अवधारणा को समझती है" [1,2,3]और इट्रेटर अनुरोधित फॉर-लूप का एक विशिष्ट उदाहरण है [1,2,3].__iter__()। एक जनरेटर किसी भी पुनरावृत्ति के समान है, जिस तरह से लिखा गया था (फ़ंक्शन सिंटैक्स के साथ) को छोड़कर।

जब आप किसी सूची से पुनरावृति का अनुरोध करते हैं, तो यह एक नया पुनरावृत्त बनाता है। हालाँकि, जब आप एक पुनरावृत्त से पुनरावृत्ति का अनुरोध करते हैं (जो आप शायद ही कभी करेंगे), तो यह आपको स्वयं की एक प्रति देता है।

इस प्रकार, अप्रत्याशित घटना में कि आप ऐसा कुछ करने में असफल हो रहे हैं ...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

... फिर याद रखें कि एक जनरेटर एक पुनरावृत्ति है ; यह है, यह एक बार उपयोग है। यदि आप इसे पुन: उपयोग करना चाहते हैं, तो आपको myRange(...)फिर से कॉल करना चाहिए । यदि आपको दो बार परिणाम का उपयोग करने की आवश्यकता है, तो परिणाम को एक सूची में परिवर्तित करें और इसे एक चर में संग्रहीत करें x = list(myRange(5))। जिन्हें पूरी तरह से एक जनरेटर का क्लोन बनाने की आवश्यकता होती है (उदाहरण के लिए, जो भयानक रूप से हैकिश मेटाप्रोग्रामिंग कर रहे हैं) का उपयोग कर सकते हैं, itertools.teeयदि आवश्यक हो तो, चूंकि कॉपी करने योग्य पुनरावृत्ति पायथन पीईपी मानकों के प्रस्ताव को स्थगित कर दिया गया है।


377

yieldपायथन में कीवर्ड क्या करता है ?

उत्तर रूपरेखा / सारांश

  • एक फ़ंक्शन yield, जिसे बुलाया जाता है, जेनरेटर लौटाता है ।
  • जनरेटर पुनरावृत्त हैं क्योंकि वे पुनरावृत्त प्रोटोकॉल को लागू करते हैं , इसलिए आप उन पर पुनरावृति कर सकते हैं।
  • एक जनरेटर को भी जानकारी भेजी जा सकती है , जो इसे वैचारिक रूप से एक कोरटाइन बनाती है
  • पायथन 3 में, आप एक जनरेटर से दूसरे में दोनों दिशाओं में प्रतिनिधि कर सकते हैं yield from
  • (परिशिष्ट शीर्ष दो सहित उत्तर की आलोचना करता है, और returnएक जनरेटर के उपयोग पर चर्चा करता है ।)

जेनरेटर:

yieldफ़ंक्शन परिभाषा के अंदर केवल कानूनी है, और फ़ंक्शन परिभाषा में शामिल करने से yieldयह जनरेटर को वापस कर देता है।

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

yieldनिम्नलिखित दो विधियों द्वारा परिभाषित इट्रेटर प्रोटोकॉल को लागू करने का एक आसान तरीका प्रदान करता है : __iter__और next(पायथन 2) या __next__(पायथन 3)। उन दोनों तरीकों से एक ऑब्जेक्ट को एक इट्रेटर बनाया जाता है जिसे आप मॉड्यूल Iteratorसे एब्सट्रैक्ट बेस क्लास के साथ टाइप-चेक कर सकते हैं collections

>>> def func():
...     yield 'I am'
...     yield 'a generator!'
... 
>>> type(func)                 # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen)                  # but it returns a generator
<type 'generator'>
>>> hasattr(gen, '__iter__')   # that's an iterable
True
>>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
True                           # implements the iterator protocol.

जनरेटर प्रकार एक उप-प्रकार है:

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

और यदि आवश्यक हो, तो हम इस तरह की जांच कर सकते हैं:

>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True

ए की एक विशेषता Iterator यह है कि एक बार समाप्त हो जाने के बाद , आप इसे पुन: उपयोग या रीसेट नहीं कर सकते:

>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]

यदि आप फिर से इसकी कार्यक्षमता का उपयोग करना चाहते हैं तो आपको एक और बनाना होगा (देखें फुटनोट 2):

>>> list(func())
['I am', 'a generator!']

उदाहरण के लिए, कोई भी प्रोग्राम को डेटा दे सकता है:

def func(an_iterable):
    for item in an_iterable:
        yield item

उपरोक्त सरल जनरेटर भी नीचे के बराबर है - जैसे कि पायथन 3.3 (और पायथन 2 में उपलब्ध नहीं है), आप इसका उपयोग कर सकते हैं yield from:

def func(an_iterable):
    yield from an_iterable

हालांकि, yield fromउप-प्रतिनिधियों को प्रतिनिधिमंडल के लिए भी अनुमति देता है, जिसे उप-कोरआउट्स के साथ सहकारी प्रतिनिधिमंडल पर निम्नलिखित अनुभाग में समझाया जाएगा।

Coroutines:

yield एक अभिव्यक्ति बनाता है जो डेटा को जनरेटर में भेजने की अनुमति देता है (फुटनोट 3 देखें)

यहां एक उदाहरण दिया गया है, receivedचर पर ध्यान दें , जो जनरेटर को भेजे जाने वाले डेटा को इंगित करेगा:

def bank_account(deposited, interest_rate):
    while True:
        calculated_interest = interest_rate * deposited 
        received = yield calculated_interest
        if received:
            deposited += received


>>> my_account = bank_account(1000, .05)

सबसे पहले, हम, builtin समारोह के साथ जनरेटर अप कतार चाहिए next। यह उपयुक्त nextया __next__विधि को कॉल करेगा , जो आपके द्वारा उपयोग किए जा रहे पायथन के संस्करण पर निर्भर करता है:

>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0

और अब हम जनरेटर में डेटा भेज सकते हैं। ( कॉलिंग की तरह ही भेजा जा रहा Noneहैnext ।):

>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5

के साथ उप-भ्रष्टाचार के लिए सहकारी प्रतिनिधिमंडल yield from

अब, याद है कि yield fromपायथन 3 में उपलब्ध है। यह हमें एक सबकोरूटीन के लिए कोरटाइन को सौंपने की अनुमति देता है:

def money_manager(expected_rate):
    under_management = yield     # must receive deposited value
    while True:
        try:
            additional_investment = yield expected_rate * under_management 
            if additional_investment:
                under_management += additional_investment
        except GeneratorExit:
            '''TODO: write function to send unclaimed funds to state'''
        finally:
            '''TODO: write function to mail tax info to client'''


def investment_account(deposited, manager):
    '''very simple model of an investment account that delegates to a manager'''
    next(manager) # must queue up manager
    manager.send(deposited)
    while True:
        try:
            yield from manager
        except GeneratorExit:
            return manager.close()

और अब हम एक उप-जनरेटर के लिए कार्यक्षमता को सौंप सकते हैं और इसे उपरोक्तानुसार जनरेटर द्वारा उपयोग किया जा सकता है:

>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6

आप PEP 380yield from में सटीक शब्दार्थ के बारे में अधिक पढ़ सकते हैं

अन्य तरीके: करीब और फेंक

closeविधि को जन्म देती GeneratorExitबिंदु समारोह निष्पादन जम गया पर। यह भी कहा जाएगा __del__ताकि आप कोई भी सफाई कोड डाल सकें जहां आप इसे संभालते हैं GeneratorExit:

>>> my_account.close()

आप एक अपवाद भी फेंक सकते हैं जिसे जनरेटर में संभाला जा सकता है या उपयोगकर्ता को वापस प्रचारित किया जा सकता है:

>>> import sys
>>> try:
...     raise ValueError
... except:
...     my_manager.throw(*sys.exc_info())
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 2, in <module>
ValueError

निष्कर्ष

मेरा मानना ​​है कि मैंने निम्नलिखित प्रश्न के सभी पहलुओं को कवर किया है:

yieldपायथन में कीवर्ड क्या करता है ?

यह पता चला है कि yieldबहुत कुछ करता है। मुझे यकीन है कि मैं इससे भी अधिक संपूर्ण उदाहरण जोड़ सकता हूं। यदि आप अधिक चाहते हैं या कुछ रचनात्मक आलोचना करते हैं, तो मुझे नीचे टिप्पणी करके बताएं।


अनुबंध:

शीर्ष / स्वीकृत उत्तर की आलोचना

  • यह एक उदाहरण के रूप में एक सूची का उपयोग करके, एक चलने योग्य बनाता है पर उलझन में है । ऊपर मेरे संदर्भ देखें, लेकिन संक्षेप में: एक पुनरावृत्त में एक पुनरावृत्ति करने वाला एक __iter__तरीका है । एक पुनरावृत्त एक .next(पायथन 2 या .__next__पायथन 3) विधि प्रदान करता है , जिसे forलूप्स द्वारा स्पष्ट रूप से कहा जाता है जब तक कि यह उठता नहीं है StopIteration, और एक बार ऐसा होने पर, यह ऐसा करना जारी रखेगा।
  • यह तब एक जनरेटर अभिव्यक्ति का उपयोग करता है यह वर्णन करने के लिए कि एक जनरेटर क्या है। चूँकि जेनरेटर इट्रेटर बनाने के लिए बस एक सुविधाजनक तरीका है , यह केवल मामले को भ्रमित करता है, और हम अभी भी yieldभाग को प्राप्त नहीं हुए हैं ।
  • में एक जनरेटर थकावट को नियंत्रित करना वह कहता है .nextविधि, जब इसके बजाय वह builtin समारोह का उपयोग करना चाहिए, next। यह परोक्ष की एक उपयुक्त परत होगी, क्योंकि उनका कोड पायथन 3 में काम नहीं करता है।
  • Itertools? यह बिल्कुल भी प्रासंगिक नहीं था yield
  • पायथन 3 में yieldनई कार्यक्षमता के साथ प्रदान करने वाले तरीकों की कोई चर्चा नहीं । शीर्ष / स्वीकृत उत्तर एक बहुत ही अपूर्ण उत्तर है।yield from

सुझाव देने वाले उत्तर का आलोचक yieldएक जनरेटर अभिव्यक्ति या समझ में आने वाले ।

व्याकरण वर्तमान में सूची बोध में किसी भी अभिव्यक्ति की अनुमति देता है।

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist

चूंकि उपज एक अभिव्यक्ति है, इसलिए इसे कुछ विशेष रूप से अच्छे उपयोग के मामले का हवाला देते हुए, इसे समझ या जनरेटर अभिव्यक्ति में उपयोग करने के लिए दिलचस्प है।

CPython कोर डेवलपर्स इसके भत्ते को कम करने पर चर्चा कर रहे हैं । यहाँ मेलिंग सूची से एक प्रासंगिक पोस्ट है:

30 जनवरी 2017 को 19:05 पर, ब्रेट तोप ने लिखा:

सूर्य पर, 29 जनवरी 2017 को 16:39 क्रेग रोड्रिग्स ने लिखा:

मैं या तो दृष्टिकोण के साथ ठीक हूँ। चीजों को छोड़ना जिस तरह से वे पायथन 3 में हैं, वह अच्छा नहीं है, IMHO।

मेरा वोट यह सिंटेक्स ईयरर्रर है क्योंकि आपको सिंटैक्स से वह नहीं मिल रहा है जिसकी आप उम्मीद करते हैं।

मैं सहमत हूँ कि यह हमारे लिए एक समझदार जगह है, क्योंकि वर्तमान व्यवहार पर भरोसा करने वाला कोई भी कोड वास्तव में बनाए रखने के लिए बहुत चालाक है।

वहाँ पाने के संदर्भ में, हम संभवतः चाहते हैं:

  • ३.W में सिंटेक्सवर्निंग या डेप्रिसिएशन
  • 2.7.x में Py3k चेतावनी
  • ३. 3.8 में सिंटैक्सएरोर

चीयर्स, निक।

- निक कॉगलन | ncoghlan at gmail.com | ब्रिस्बेन, ऑस्ट्रेलिया

इसके अलावा, एक उत्कृष्ट मुद्दा (10544) है जो इस दिशा में इंगित करता है कि यह कभी भी एक अच्छा विचार नहीं है (PyPy, Python द्वारा Python में लिखा गया कार्यान्वयन, पहले से ही वाक्यविन्यास चेतावनी दे रहा है।)

बॉटम लाइन, जब तक कि सीपीथॉन के डेवलपर्स हमें अन्यथा नहीं बताते हैं: जनरेटर की अभिव्यक्ति या समझ में न डालें yield

returnएक जनरेटर में बयान

में अजगर 2 :

जनरेटर फ़ंक्शन में, returnकथन को शामिल करने की अनुमति नहीं है expression_list। उस संदर्भ में, एक नंगे returnइंगित करता है कि जनरेटर किया जाता है और StopIterationउठाए जाने का कारण होगा ।

एक expression_listमूल रूप से अल्पविराम के द्वारा अलग भाव के किसी भी संख्या है - अनिवार्य रूप से, अजगर 2 में, आप के साथ जनरेटर बंद कर सकते हैं return, लेकिन आप एक मूल्य वापस नहीं लौट सकते।

में अजगर 3 :

एक जनरेटर फ़ंक्शन में, returnबयान इंगित करता है कि जनरेटर किया जाता है और StopIterationउठाए जाने का कारण होगा । लौटाया गया मान (यदि कोई हो) निर्माण करने के लिए एक तर्क के रूप में प्रयोग किया StopIterationजाता है और StopIteration.valueविशेषता बन जाता है ।

फुटनोट

  1. पायथन में जनरेटर की अवधारणा को पेश करने के प्रस्ताव में सीएलयू, सथर, और आइकन भाषाओं का उल्लेख किया गया था। सामान्य विचार यह है कि एक फ़ंक्शन आंतरिक स्थिति को बनाए रख सकता है और उपयोगकर्ता द्वारा मांग पर मध्यवर्ती डेटा अंक प्राप्त कर सकता है। इसने पायथन थ्रेडिंग सहित अन्य तरीकों के प्रदर्शन में श्रेष्ठ होने का वादा किया , जो कुछ प्रणालियों पर भी उपलब्ध नहीं है।

  2. इसका मतलब है, उदाहरण के लिए, कि xrangeऑब्जेक्ट्स ( rangeपायथन 3 में) Iteratorएस नहीं हैं , भले ही वे पुनरावृत्त हों, क्योंकि उनका पुन: उपयोग किया जा सकता है। सूचियों की तरह, उनके __iter__तरीके पुनरावृत्त वस्तुएं लौटाते हैं।

  3. yieldमूल रूप से एक बयान के रूप में पेश किया गया था, जिसका अर्थ है कि यह केवल एक कोड ब्लॉक में एक पंक्ति की शुरुआत में दिखाई दे सकता है। अब yieldएक उपज अभिव्यक्ति बनाता है। https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt यह परिवर्तन प्रस्तावित किया गया था कि कोई उपयोगकर्ता जनरेटर में डेटा भेजने की अनुमति उसी तरह दे सकता है जैसे कोई इसे प्राप्त कर सकता है। डेटा भेजने के लिए, किसी को इसे किसी चीज़ को निर्दिष्ट करने में सक्षम होना चाहिए, और उसके लिए, एक बयान सिर्फ काम नहीं करेगा।


328

yieldजैसा है return- आप इसे (एक जनरेटर के रूप में) जो कुछ भी बताते हैं, उसे वापस कर देता है। अंतर यह है कि अगली बार जब आप जनरेटर को कॉल करते हैं, तो अंतिम कॉल से yieldस्टेटमेंट तक निष्पादन शुरू होता है । वापसी के विपरीत, स्टैक फ्रेम को तब साफ नहीं किया जाता है जब एक उपज होती है, हालांकि नियंत्रण वापस कॉलर को स्थानांतरित कर दिया जाता है, इसलिए इसका राज्य अगली बार फ़ंक्शन को कॉल करने पर फिर से शुरू होगा।

आपके कोड के मामले में, फ़ंक्शन get_child_candidatesएक पुनरावृत्त की तरह काम कर रहा है ताकि जब आप अपनी सूची का विस्तार करें, तो यह नई सूची में एक समय में एक तत्व जोड़ता है।

list.extendजब तक यह समाप्त नहीं हो जाता है तब तक एक पुनरावृत्तिकर्ता को बुलाता है। आपके द्वारा पोस्ट किए गए कोड नमूने के मामले में, यह केवल एक टपल वापस करने के लिए बहुत स्पष्ट होगा और सूची में जोड़ा जाएगा।


107
यह करीब है, लेकिन सही नहीं है। हर बार जब आप इसमें एक पैदावार स्टेटमेंट के साथ एक फ़ंक्शन कहते हैं, तो यह एक नया जनरेटर ऑब्जेक्ट देता है। यह केवल तभी होता है जब आप उस जनरेटर की .next () विधि को कहते हैं जो अंतिम उपज के बाद क्रियान्वित होती है।
कुरोस्च

239

उल्लेख करने के लिए एक अतिरिक्त बात है: एक ऐसा फल जो पैदावार वास्तव में समाप्त नहीं होता है। मैंने इस तरह कोड लिखा है:

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

फिर मैं इसे इस तरह से अन्य कोड में उपयोग कर सकता हूं:

for f in fib():
    if some_condition: break
    coolfuncs(f);

यह वास्तव में कुछ समस्याओं को आसान बनाने में मदद करता है, और कुछ चीजों को काम करना आसान बनाता है।


233

उन लोगों के लिए जो न्यूनतम कामकाजी उदाहरण पसंद करते हैं, इस इंटरैक्टिव पायथन सत्र पर ध्यान दें:

>>> def f():
...   yield 1
...   yield 2
...   yield 3
... 
>>> g = f()
>>> for i in g:
...   print(i)
... 
1
2
3
>>> for i in g:
...   print(i)
... 
>>> # Note that this time nothing was printed

208

टी एल; डॉ

इसके अलावा:

def square_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

यह करो:

def square_yield(n):
    for x in range(n):
        y = x * x
        yield y                           # with this one.

जब भी आप अपने आप को खरोंच से एक सूची का निर्माण करते हुए पाते हैं, yieldइसके बजाय प्रत्येक टुकड़ा।

उपज के साथ यह मेरा पहला "अहा" पल था।


yieldकहने का एक मीठा तरीका है

सामान की एक श्रृंखला का निर्माण

समान व्यवहार:

>>> for square in square_list(4):
...     print(square)
...
0
1
4
9
>>> for square in square_yield(4):
...     print(square)
...
0
1
4
9

अलग व्यवहार:

यील्ड सिंगल-पास है : आप केवल एक बार इसके माध्यम से पुनरावृति कर सकते हैं। जब किसी फ़ंक्शन की उपज होती है तो हम इसे जनरेटर फ़ंक्शन कहते हैं । और एक पुनरावृत्ति करने वाला होता है। वे शर्तें प्रकट कर रहे हैं। हम एक कंटेनर की सुविधा खो देते हैं, लेकिन एक श्रृंखला की शक्ति हासिल करते हैं जो आवश्यकतानुसार गणना की जाती है, और मनमाने ढंग से लंबे समय तक।

यील्ड आलसी है , यह गणना को बंद कर देता है। जब आप इसे कहते हैं, तो इसमें उपज के साथ एक फ़ंक्शन वास्तव में बिल्कुल भी निष्पादित नहीं होता है। यह एक पुनरावृत्त वस्तु लौटाता है जो याद करता है कि वह कहाँ छोड़ दिया है। हर बार जब आप next()इट्रेटर पर कॉल करते हैं (यह फॉर-लूप में होता है) अगली उपज के लिए निष्पादन इंच। returnStopIteration उठाता है और श्रृंखला समाप्त करता है (यह फॉर-लूप का प्राकृतिक अंत है)।

उपज बहुमुखी है । डेटा को सभी को एक साथ संग्रहीत करने की आवश्यकता नहीं है, इसे एक बार में उपलब्ध कराया जा सकता है। यह अनंत हो सकता है।

>>> def squares_all_of_them():
...     x = 0
...     while True:
...         yield x * x
...         x += 1
...
>>> squares = squares_all_of_them()
>>> for _ in range(4):
...     print(next(squares))
...
0
1
4
9

यदि आपको कई पास चाहिए और श्रृंखला बहुत लंबी नहीं है, तो बस list()उस पर कॉल करें :

>>> list(square_yield(4))
[0, 1, 4, 9]

शब्द का शानदार विकल्प yieldक्योंकि दोनों अर्थ लागू होते हैं:

उपज - उपज या प्रदान (कृषि में)

... श्रृंखला में अगला डेटा प्रदान करते हैं।

उपज - रास्ता दें या त्याग दें (राजनीतिक सत्ता में)

... पुनरावृत्ति सीपीयू निष्पादन जब तक पुनरावृत्ति अग्रिम।


194

यील्ड आपको एक जनरेटर देता है।

def get_odd_numbers(i):
    return range(1, i, 2)
def yield_odd_numbers(i):
    for x in range(1, i, 2):
       yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5

जैसा कि आप देख सकते हैं, पहले मामले fooमें एक ही बार में पूरी सूची स्मृति में है। 5 तत्वों वाली सूची के लिए यह कोई बड़ी बात नहीं है, लेकिन अगर आप 5 मिलियन की सूची चाहते हैं तो क्या होगा? न केवल यह एक विशाल मेमोरी खाने वाला है, यह उस फ़ंक्शन को बनाने के लिए बहुत समय खर्च करता है।

दूसरे मामले में, barबस आपको एक जनरेटर देता है। एक जनरेटर एक चलने योग्य है - जिसका अर्थ है कि आप इसे forलूप में उपयोग कर सकते हैं , आदि, लेकिन प्रत्येक मूल्य को केवल एक बार एक्सेस किया जा सकता है। सभी मान भी एक ही समय में मेमोरी में संग्रहीत नहीं होते हैं; जनरेटर ऑब्जेक्ट "याद" करता है, जहां यह पिछली बार जब आपने इसे कॉल किया था तब लूपिंग में था - इस तरह, यदि आप 50 बिलियन तक की गणना (कहने) का उपयोग कर रहे हैं, तो आपको 50 बिलियन तक गिनती करने की आवश्यकता नहीं है एक बार में और 50 बिलियन नंबरों को स्टोर करने के लिए।

फिर, यह एक बहुत ही वंचित उदाहरण है, यदि आप वास्तव में 50 बिलियन की गिनती करना चाहते हैं तो आप शायद इटर्टूल का उपयोग करेंगे। :)

यह जनरेटर का सबसे सरल उपयोग मामला है। जैसा कि आपने कहा, इसका उपयोग कुशल क्रमपरिवर्तन लिखने के लिए किया जा सकता है, उपज का उपयोग करके कॉल स्टैक के माध्यम से कुछ प्रकार के स्टैक चर का उपयोग करने के बजाय। जेनरेटर का उपयोग विशेष ट्री ट्रैवर्सल और अन्य सभी तरीकों के लिए भी किया जा सकता है।


बस एक नोट - पायथन 3 में, rangeएक सूची के बजाय एक जनरेटर भी देता है, इसलिए आपको एक समान विचार दिखाई देगा, सिवाय इसके कि __repr__/ __str__इस मामले में एक अच्छे परिणाम दिखाने के लिए ओवरराइड किया जाता है range(1, 10, 2)
इट्नोटली।

189

यह एक जेनरेटर लौटा रहा है। मैं पायथन से विशेष रूप से परिचित नहीं हूं, लेकिन मेरा मानना ​​है कि यदि आप उन लोगों से परिचित हैं तो यह C # के पुनरावृत्तियों के ब्लॉक के समान है

प्रमुख विचार यह है कि संकलक / दुभाषिया / जो कुछ भी चालबाजी करता है ताकि जहां तक ​​कॉलर का संबंध है, वे अगले फोन कर सकें () और यह रिटर्न मान रखेगा - मानो जनरेटर विधि रोक दी गई थी । अब स्पष्ट रूप से आप वास्तव में एक विधि को "विराम" नहीं दे सकते हैं, इसलिए कंपाइलर आपके लिए एक राज्य मशीन बनाता है ताकि आपको याद रहे कि आप वर्तमान में कहां हैं और स्थानीय चर आदि क्या दिखते हैं। यह अपने आप को एक इटिटर लिखने से बहुत आसान है।


167

एक प्रकार का उत्तर है जो मुझे नहीं लगता कि अभी तक कई महान उत्तरों में से एक दिया गया है, जो वर्णन करता है कि जनरेटर का उपयोग कैसे करें। यहाँ प्रोग्रामिंग भाषा सिद्धांत उत्तर है:

yieldअजगर में बयान एक जनरेटर देता है। पाइथन में एक जनरेटर एक फ़ंक्शन है जो निरंतरता (और विशेष रूप से कोरटाइन का एक प्रकार है, लेकिन निरंतरता सामान्य तंत्र को दर्शाता है कि क्या चल रहा है)।

प्रोग्रामिंग भाषाओं के सिद्धांत में निरंतरता एक बहुत अधिक मौलिक प्रकार की संगणना है, लेकिन वे अक्सर उपयोग नहीं की जाती हैं, क्योंकि वे कारण के बारे में बहुत कठिन हैं और उन्हें लागू करना बहुत कठिन है। लेकिन एक निरंतरता क्या है, इसका विचार सीधा है: यह एक संगणना की स्थिति है जो अभी तक समाप्त नहीं हुई है। इस स्थिति में, चर के वर्तमान मान, जो ऑपरेशन अभी तक किए जाने हैं, और इसी तरह, सहेजे जाते हैं। फिर कुछ बिंदु पर बाद में कार्यक्रम में निरंतरता को लागू किया जा सकता है, जैसे कि कार्यक्रम के चर उस राज्य में रीसेट हो जाते हैं और जो कार्य सहेजे गए थे, वे किए जाते हैं।

निरंतरता, इस अधिक सामान्य रूप में, दो तरीकों से लागू की जा सकती है। में call/ccजिस तरह से, कार्यक्रम के ढेर सचमुच सहेजा जाता है और फिर जब निरंतरता शुरू हो जाती है, ढेर बहाल है।

निरंतर उत्तीर्ण शैली (सीपीएस) में, निरंतरता केवल सामान्य कार्य हैं (केवल उन भाषाओं में जहां फ़ंक्शन प्रथम श्रेणी हैं) जो प्रोग्रामर स्पष्ट रूप से प्रबंधित करता है और सबरूटीन्स के लिए गुजरता है। इस शैली में, प्रोग्राम स्टेट को क्लोज़र के बजाय क्लोज़र के बजाय क्लोजर (और उनमें होने वाले वैरिएबल में होने वाले वेरिएबल्स) का प्रतिनिधित्व किया जाता है। नियंत्रण प्रवाह को प्रबंधित करने वाले कार्य तर्क के रूप में निरंतरता को स्वीकार करते हैं (सीपीएस के कुछ रूपों में, फ़ंक्शन कई निरंतरता को स्वीकार कर सकते हैं) और नियंत्रण प्रवाह में हेरफेर करके उन्हें केवल कॉल करके और बाद में वापस लौटाएं। निरंतर गुजर शैली का एक बहुत ही सरल उदाहरण इस प्रकार है:

def save_file(filename):
  def write_file_continuation():
    write_stuff_to_file(filename)

  check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)

इस (बहुत ही सरलीकृत) उदाहरण में, प्रोग्रामर वास्तव में फ़ाइल को एक निरंतरता में लिखने के संचालन को बचाता है (जो संभवतः बाहर लिखने के लिए कई विवरणों के साथ एक बहुत ही जटिल ऑपरेशन हो सकता है), और फिर उस निरंतरता (यानी, पहले के रूप में) को पारित करता है एक अन्य ऑपरेटर को क्लास क्लोजर) जो कुछ और प्रसंस्करण करता है, और फिर यदि आवश्यक हो तो इसे कॉल करता है। (मैं वास्तविक जीयूआई प्रोग्रामिंग में इस डिजाइन पैटर्न का बहुत उपयोग करता हूं, या तो क्योंकि यह मुझे जीयूआई घटनाओं के ट्रिगर के बाद नियंत्रण प्रवाह का प्रबंधन करने के लिए कोड की लाइनें या अधिक महत्वपूर्ण बात बचाता है।)

इस पद के बाकी हिस्सों को सामान्यता के नुकसान के बिना, सीपीएस के रूप में जारी रखने की अवधारणा होगी, क्योंकि यह समझने और पढ़ने में बहुत आसान है।


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

def f():
  while True:
    yield 4

यह स्पष्ट रूप से एक उचित चलने योग्य है जिसका व्यवहार अच्छी तरह से परिभाषित किया गया है - हर बार जब जनरेटर इसे खत्म करता है, तो यह 4 (और हमेशा के लिए) लौटता है। लेकिन यह शायद चलने योग्य प्रकार का चलने योग्य नहीं है जो पुनरावृत्तियों (यानी, for x in collection: do_something(x)) के बारे में सोचते समय ध्यान में आता है । यह उदाहरण जनरेटर की शक्ति को दिखाता है: यदि कुछ भी एक पुनरावृत्त है, तो एक जनरेटर अपनी पुनरावृत्ति की स्थिति को बचा सकता है।

दोहराए जाने के लिए: निरंतरता एक कार्यक्रम के ढेर की स्थिति को बचा सकती है और जनरेटर पुनरावृत्ति की स्थिति को बचा सकते हैं। इसका अर्थ है कि जनरेटर की तुलना में निरंतरता बहुत अधिक शक्तिशाली है, लेकिन यह भी कि जनरेटर बहुत अधिक हैं, बहुत आसान है। वे भाषा डिजाइनर को लागू करने के लिए आसान हैं, और वे प्रोग्रामर का उपयोग करने के लिए आसान हैं (यदि आपके पास जलने के लिए कुछ समय है, तो इस पृष्ठ को निरंतरता और कॉल / सीसी के बारे में पढ़ने और समझने की कोशिश करें )।

लेकिन आप आसानी से लागू कर सकते हैं (और अवधारणा) जनरेटर को एक सरल, विशिष्ट मामले के रूप में जारी रखा जा सकता है:

जब भी yieldबुलाया जाता है, यह फ़ंक्शन को एक निरंतरता वापस करने के लिए कहता है। जब फ़ंक्शन को फिर से कॉल किया जाता है, तो यह जहां भी छोड़ा जाता है वहां से शुरू होता है। तो, छद्म- pseudocode में (यानी, pseudocode नहीं, लेकिन कोड नहीं) जनरेटर की nextविधि मूल रूप से निम्नानुसार है:

class Generator():
  def __init__(self,iterable,generatorfun):
    self.next_continuation = lambda:generatorfun(iterable)

  def next(self):
    value, next_continuation = self.next_continuation()
    self.next_continuation = next_continuation
    return value

जहां yieldकीवर्ड वास्तव में वास्तविक जनरेटर फ़ंक्शन के लिए वाक्यात्मक चीनी है, मूल रूप से कुछ इस तरह है:

def generatorfun(iterable):
  if len(iterable) == 0:
    raise StopIteration
  else:
    return (iterable[0], lambda:generatorfun(iterable[1:]))

याद रखें कि यह सिर्फ छद्म कोड है और पायथन में जनरेटर का वास्तविक कार्यान्वयन अधिक जटिल है। लेकिन यह समझने के लिए एक अभ्यास के रूप में कि क्या चल रहा है, yieldकीवर्ड के उपयोग के बिना जनरेटर वस्तुओं को लागू करने के लिए निरंतर गुजर शैली का उपयोग करने का प्रयास करें ।


152

यहाँ स्पष्ट भाषा में एक उदाहरण है। मैं निम्न-स्तरीय पायथन अवधारणाओं के लिए उच्च-स्तरीय मानव अवधारणाओं के बीच एक पत्राचार प्रदान करूंगा।

मैं संख्याओं के अनुक्रम पर काम करना चाहता हूं, लेकिन मैं उस क्रम के निर्माण के साथ अपने आप को परेशान नहीं करना चाहता, मैं केवल उस ऑपरेशन पर ध्यान केंद्रित करना चाहता हूं जो मैं करना चाहता हूं। तो, मैं निम्नलिखित कार्य करता हूं:

  • मैं आपको फोन करता हूं और आपको बताता हूं कि मुझे उन संख्याओं का एक क्रम चाहिए जो एक विशिष्ट तरीके से निर्मित होता है, और मैं आपको बताता हूं कि एल्गोरिथ्म क्या है।
    यह चरण defजेनरेटर फ़ंक्शन, अर्थात फ़ंक्शन युक्त, से मेल खाता है yield
  • कुछ समय बाद, मैं आपको बताता हूं, "ठीक है, मुझे संख्याओं का क्रम बताने के लिए तैयार हो जाओ"।
    यह चरण जनरेटर फ़ंक्शन को कॉल करने से मेल खाता है जो जनरेटर ऑब्जेक्ट लौटाता है। ध्यान दें कि आप मुझे अभी तक कोई संख्या नहीं बताते हैं; आप बस अपने कागज और पेंसिल को पकड़ो।
  • मैं आपसे पूछता हूं, "मुझे अगला नंबर बताएं", और आप मुझे पहला नंबर बताएं; उसके बाद, आप मुझे अगले नंबर के लिए पूछने के लिए प्रतीक्षा करें। यह याद रखना आपका काम है कि आप कहां थे, आपने पहले से क्या नंबर कहा है, और अगला नंबर क्या है। मैं विवरण के बारे में परवाह नहीं है।
    यह कदम .next()जनरेटर ऑब्जेक्ट पर कॉल करने से मेल खाती है ।
  • ... पिछले चरण को दोहराएं, जब तक ...
  • अंततः, आप समाप्त हो सकते हैं। तुम मुझे एक नंबर नहीं बताओ; तुम बस चिल्लाओ, "अपने घोड़ों को पकड़ो! मैं काम कर रहा हूँ! कोई और संख्या नहीं!"
    यह कदम जेनरेटर ऑब्जेक्ट से संबंधित है जो उसकी नौकरी को समाप्त करता है, और एक StopIterationअपवाद को बढ़ाता है जनरेटर फ़ंक्शन को अपवाद को बढ़ाने की आवश्यकता नहीं है। यह स्वचालित रूप से उठाया जाता है जब फ़ंक्शन समाप्त होता है या जारी करता है return

यह वह है जो एक जनरेटर करता है (एक फ़ंक्शन जिसमें एक होता है yield); यह निष्पादित करना शुरू कर देता है, जब भी यह करता है, रोक देता है yield, और जब .next()मूल्य पूछा जाता है तो यह उस बिंदु से जारी रहता है जब यह अंतिम था। यह पायथन के पुनरावृत्त प्रोटोकॉल के साथ डिजाइन द्वारा पूरी तरह से फिट बैठता है, जो कि क्रमिक रूप से मूल्यों का अनुरोध करने का वर्णन करता है।

forपायथन प्रोटोकॉल में सबसे प्रसिद्ध उपयोगकर्ता पायथन में कमांड है। तो, जब भी आप एक:

for item in sequence:

इससे कोई फर्क नहीं पड़ता कि sequenceक्या एक सूची, एक स्ट्रिंग, एक शब्दकोश या एक जनरेटर वस्तु जैसा ऊपर वर्णित है; परिणाम एक ही है: आप एक एक करके एक क्रम से आइटम पढ़ते हैं।

ध्यान दें कि defएक फंक्शन को सम्मिलित करना जिसमें एक yieldकीवर्ड होता है एक जनरेटर बनाने का एकमात्र तरीका नहीं है; यह सिर्फ एक बनाने का सबसे आसान तरीका है।

अधिक सटीक जानकारी के लिए, पैटरन प्रलेखन में पुनरावृत्त प्रकार , उपज कथन और जनरेटर के बारे में पढ़ें ।


130

हालांकि बहुत सारे उत्तर बताते हैं कि आप yieldजनरेटर बनाने के लिए उपयोग क्यों करेंगे , इसके लिए अधिक उपयोग हैं yield। एक कोरटाइन बनाना काफी आसान है, जो कोड के दो ब्लॉकों के बीच जानकारी के पारित होने में सक्षम बनाता है। मैं yieldजनरेटर का उपयोग करने के बारे में पहले से दिए गए किसी भी बढ़िया उदाहरण को नहीं दोहराऊंगा ।

yieldनिम्नलिखित कोड में क्या करता है, यह समझने में मदद करने के लिए , आप अपनी उंगली का उपयोग करके किसी भी कोड के माध्यम से चक्र का पता लगा सकते हैं yield। हर बार अपनी उंगली हिट yield, तो आप एक के लिए इंतजार करना पड़ता है nextया एक sendदर्ज किए जाने की। जब एक nextको बुलाया जाता है, तो आप कोड के माध्यम से ट्रेस करते हैं जब तक कि आप हिट नहीं करते yield... दाईं ओर के कोड yieldका मूल्यांकन किया जाता है और कॉलर को वापस कर दिया जाता है ... तब आप प्रतीक्षा करते हैं। जब nextफिर से कॉल किया जाता है, तो आप कोड के माध्यम से एक और लूप निष्पादित करते हैं। हालाँकि, आप ध्यान देंगे कि एक coroutine में, yieldइसका उपयोग send… के साथ भी किया जा सकता है , जो कि कॉल करने वाले से येलियर फंक्शन में वैल्यू भेजेगा । यदि एक sendदिया जाता है, तोyieldभेजे गए मूल्य को प्राप्त करता है, और इसे बाएं हाथ की ओर से बाहर थूकता है ... फिर कोड के माध्यम से ट्रेस तब तक आगे बढ़ता है जब तक कि आप yieldफिर से हिट नहीं करते (अंत में मान लौटाते हैं, जैसे कि nextकहा जाता था)।

उदाहरण के लिए:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

प्यारा! एक ट्रम्पोलिन (लिस्प अर्थ में)। नहीं अक्सर उन लोगों को देखता है!
00prometheus

129

एक और yieldउपयोग और अर्थ है (पायथन 3.3 के बाद से):

yield from <expr>

से पीईपी 380 - Delegating के लिए एक Subgenerator को सिंटेक्स :

एक जनरेटर के लिए एक सिंटैक्स प्रस्तावित है जो इसके संचालन के हिस्से को दूसरे जनरेटर को सौंपने के लिए है। यह 'उपज' वाले कोड के एक हिस्से को फैक्टर आउट करके दूसरे जनरेटर में रखने की अनुमति देता है। इसके अतिरिक्त, उपसमूह को एक मान के साथ वापस जाने की अनुमति है, और मूल्य को प्रतिनिधि जनरेटर को उपलब्ध कराया जाता है।

नया सिंटैक्स अनुकूलन के लिए कुछ अवसरों को भी खोलता है जब एक जनरेटर दूसरे द्वारा उत्पादित मानों को पुन: प्राप्त करता है।

इसके अलावा यह पेश करेगा (पायथन 3.5 के बाद से):

async def new_coroutine(data):
   ...
   await blocking_action()

एक नियमित जनरेटर (आज yieldदोनों में उपयोग किया जाता है) के साथ कॉरआउट्स से बचने के लिए ।


117

सभी महान जवाब, हालांकि newbies के लिए थोड़ा मुश्किल है।

मुझे लगता है कि आपने returnबयान सीख लिया है ।

एक सादृश्य के रूप में, returnऔर yieldजुड़वां हैं। return'वापसी और रोक' का मतलब है जबकि 'उपज' का मतलब है 'वापसी, लेकिन जारी रखें'

  1. के साथ एक num_list प्राप्त करने का प्रयास करें return
def num_list(n):
    for i in range(n):
        return i

चलाओ:

In [5]: num_list(3)
Out[5]: 0

देखें, आपको उनकी सूची के बजाय केवल एक ही संख्या मिलती है। returnआपको कभी भी खुशी से जीतने की अनुमति नहीं देता है, बस एक बार लागू करता है और छोड़ देता है।

  1. वहाँ आता है yield

इसके returnसाथ बदलें yield:

In [10]: def num_list(n):
    ...:     for i in range(n):
    ...:         yield i
    ...:

In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]

अब, आप सभी नंबर प्राप्त करने के लिए जीत गए।

returnजिसकी तुलना में आप एक बार दौड़ते हैं और रुक जाते हैं, yieldआपके द्वारा नियत समय को चलाते हैं। आप के returnरूप में return one of them, और के yieldरूप में व्याख्या कर सकते हैं return all of them। इसे कहते हैं iterable

  1. एक और कदम हम yieldबयान को फिर से लिख सकते हैंreturn
In [15]: def num_list(n):
    ...:     result = []
    ...:     for i in range(n):
    ...:         result.append(i)
    ...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]

इसके बारे में मूल है yield

सूची returnआउटपुट और ऑब्जेक्ट yieldआउटपुट के बीच का अंतर है:

आपको हमेशा एक सूची ऑब्जेक्ट से [0, 1, 2] प्राप्त होगा लेकिन केवल yieldएक बार ' ऑब्जेक्ट आउटपुट' से उन्हें पुनः प्राप्त कर सकता है । इसलिए, इसमें एक नया नाम generatorऑब्जेक्ट है जैसा कि प्रदर्शित किया गया है Out[11]: <generator object num_list at 0x10327c990>

निष्कर्ष में, इसे रूपांतरित करने के लिए एक रूपक के रूप में:

  • returnऔर yieldजुड़वाँ हैं
  • listऔर generatorजुड़वाँ हैं

यह समझ में आता है, लेकिन एक बड़ा अंतर यह है कि आपके पास फ़ंक्शन / पद्धति में कई उपज हो सकती हैं। सादृश्य पूरी तरह से उस बिंदु पर टूट जाता है। यील्ड एक फ़ंक्शन में अपनी जगह को याद करता है, इसलिए अगली बार जब आप अगली कॉल करते हैं (), तो आपका फ़ंक्शन अगले पर जारी रहता है yield। यह महत्वपूर्ण है, मुझे लगता है, और व्यक्त किया जाना चाहिए।
माइक एस

104

यहाँ कुछ पायथन उदाहरण हैं कि वास्तव में जनरेटर को कैसे लागू किया जाए जैसे कि पायथन ने उनके लिए सिंटैक्टिक चीनी प्रदान नहीं की:

पायथन जनरेटर के रूप में:

from itertools import islice

def fib_gen():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

जनरेटर के बजाय लेक्सिकल क्लोजर का उपयोग करना

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

जनरेटर के बजाय ऑब्जेक्ट क्लोजर का उपयोग करना (क्योंकि ClosuresAndObjectsAreEquivalent )

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)

97

मैं "जेनरेटर के त्वरित विवरण के लिए बेज़ले के 'पायथन: आवश्यक संदर्भ' के पृष्ठ 19 को पढ़ने जा रहा था, लेकिन इतने सारे लोगों ने पहले से ही अच्छे विवरण पोस्ट किए हैं।

इसके अलावा, ध्यान दें कि yieldजनरेटर कार्यों में उनके उपयोग के दोहरे के रूप में कोरटाइन में इस्तेमाल किया जा सकता है। यद्यपि यह आपके कोड स्निपेट के समान उपयोग नहीं है, लेकिन (yield)एक फ़ंक्शन में एक अभिव्यक्ति के रूप में उपयोग किया जा सकता है। जब एक कॉलर विधि का उपयोग करके विधि को एक मूल्य भेजता है send(), तो कोराउटीन तब तक निष्पादित करेगा जब तक कि अगले (yield)बयान का सामना नहीं किया जाता है।

जेनरेटर और कोरआउट्स डेटा-फ्लो टाइप एप्लिकेशन सेट करने का एक अच्छा तरीका है। मैंने सोचा कि yieldकार्यों में कथन के अन्य उपयोग के बारे में जानना सार्थक होगा ।


97

एक प्रोग्रामिंग दृष्टिकोण से, पुनरावृत्तियों को थ्रक्स के रूप में लागू किया जाता है

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

http://en.wikipedia.org/wiki/Message_passing

" अगला " एक संदेश है जो एक बंद करने के लिए भेजा गया है, जो " iter " कॉल द्वारा बनाया गया है ।

इस गणना को लागू करने के बहुत सारे तरीके हैं। मैंने म्यूटेशन का उपयोग किया है, लेकिन वर्तमान मूल्य और अगले उपज को वापस करके, इसे म्यूटेशन के बिना करना आसान है।

यहाँ एक प्रदर्शन है जो R6RS की संरचना का उपयोग करता है, लेकिन शब्दार्थ पाइथन के समान है। यह संगणना का एक ही मॉडल है, और केवल पायथन में इसे फिर से लिखने के लिए सिंटैक्स में बदलाव की आवश्यकता है।

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->

84

ये रहा एक सरल उदाहरण:

def isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

आउटपुट:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

मैं पायथन डेवलपर नहीं हूं, लेकिन यह मुझे दिखता है yield कार्यक्रम प्रवाह की स्थिति रखता है और अगला लूप "उपज" की स्थिति से शुरू होता है। ऐसा लगता है कि यह उस स्थिति में इंतजार कर रहा है, और उससे ठीक पहले, एक मूल्य बाहर लौट रहा है, और अगली बार काम करना जारी है।

यह एक दिलचस्प और अच्छी क्षमता है: डी


तुम सही हो। लेकिन प्रवाह पर क्या प्रभाव पड़ता है जो "उपज" के व्यवहार को देखना है? मैं गणित के नाम पर एल्गोरिथ्म को बदल सकता हूं। क्या यह "उपज" के विभिन्न मूल्यांकन प्राप्त करने में मदद करेगा?
इंगिन OZTURK

68

यहाँ एक मानसिक छवि है कि क्या yieldकरता है।

मैं एक थ्रेड होने के बारे में सोचना पसंद करता हूं (तब भी जब यह उस तरह से लागू नहीं होता है)।

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

एक yieldफ़ंक्शन के साथ , जब इसका कोड चलना शुरू हो जाता है (यानी फ़ंक्शन को कॉल करने के बाद, जेनरेटर ऑब्जेक्ट को लौटाया जाता है, जिसकी next()विधि फिर लागू की जाती है), यह इसी तरह अपने स्थानीय चर को स्टैक पर रखता है और थोड़ी देर के लिए गणना करता है। लेकिन तब, जब यह yieldस्टेटमेंट को हिट करता है, तो स्टैक के अपने हिस्से को साफ़ करने और वापस लौटने से पहले, यह अपने स्थानीय चर का स्नैपशॉट लेता है और उन्हें जनरेटर ऑब्जेक्ट में संग्रहीत करता है। यह उस स्थान को भी लिखता है जहां यह वर्तमान में अपने कोड में है (यानी विशेष yieldविवरण)।

तो यह एक प्रकार का फ्रोजन फंक्शन है जो जनरेटर पर लटका हुआ है।

जब next()बाद में कहा जाता है, तो यह स्टैक पर फ़ंक्शन के सामान को पुनः प्राप्त करता है और इसे फिर से एनिमेट करता है। यह कार्य उस जगह से जारी रहता है, जहां से यह छोड़ दिया गया है, इस तथ्य से बेखबर है कि इसने अभी तक कोल्ड स्टोरेज में एक अनंत काल बिताया था।

निम्नलिखित उदाहरणों की तुलना करें:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

जब हम दूसरे फ़ंक्शन को कॉल करते हैं, तो यह पहले से बहुत अलग व्यवहार करता है। yieldबयान नहीं पहुंचा जा सकता हो सकता है, लेकिन अगर यह वर्तमान कहीं भी है, यह हम क्या साथ काम कर रहे की प्रकृति बदल जाता है।

>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>

कॉलिंग yielderFunction()अपना कोड नहीं चलाता है, लेकिन एक जनरेटर को कोड से बाहर कर देता है। (शायद yielderपठनीयता के लिए उपसर्ग के साथ ऐसी चीजों को नाम देना एक अच्छा विचार है।)

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

gi_codeऔर gi_frameक्षेत्रों जहां जमे हुए राज्य संग्रहीत किया जाता है कर रहे हैं। उनके साथ खोज करते हुए dir(..), हम इस बात की पुष्टि कर सकते हैं कि ऊपर का हमारा मानसिक मॉडल विश्वसनीय है।


59

जैसा कि हर उत्तर से पता चलता है, yieldएक अनुक्रम जनरेटर बनाने के लिए उपयोग किया जाता है। इसका उपयोग गतिशील रूप से कुछ अनुक्रम उत्पन्न करने के लिए किया जाता है। उदाहरण के लिए, किसी नेटवर्क पर लाइन द्वारा एक फ़ाइल लाइन पढ़ते समय, आप yieldनिम्नानुसार फ़ंक्शन का उपयोग कर सकते हैं :

def getNextLines():
   while con.isOpen():
       yield con.read()

आप इसे अपने कोड में निम्नानुसार उपयोग कर सकते हैं:

for line in getNextLines():
    doSomeThing(line)

निष्पादन नियंत्रण स्थानांतरण गच्चा

जब निष्पादन किया जाता है तो निष्पादन नियंत्रण getNextLines () से forलूप में स्थानांतरित हो जाएगा । इस प्रकार, हर बार getNextLines () को लागू किया जाता है, निष्पादन उस बिंदु से शुरू होता है जहां इसे पिछली बार रोका गया था।

इस प्रकार, संक्षेप में, निम्नलिखित कोड के साथ एक फ़ंक्शन

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

छप जाएगा

"first time"
"second time"
"third time"
"Now some useful value 12"

59

समझने के लिए एक आसान उदाहरण कि यह क्या है: yield

def f123():
    for _ in range(4):
        yield 1
        yield 2


for i in f123():
    print (i)

आउटपुट है:

1 2 1 2 1 2 1 2

5
क्या आप उस आउटपुट के बारे में निश्चित हैं? यदि आप उस प्रिंट स्टेटमेंट का उपयोग कर भागते हैं, तो केवल एक ही लाइन पर प्रिंट नहीं किया जा सकता है print(i, end=' ')? अन्यथा, मेरा मानना ​​है कि डिफ़ॉल्ट व्यवहार प्रत्येक संख्या को एक नई लाइन पर
रखेगा

@ user9074332, आप सही कह रहे हैं, लेकिन इसे समझने की सुविधा के लिए एक लाइन पर लिखा गया है
गवरील कोहेन

57

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

जब एक अजगर समारोह में एक के yieldबजाय प्रयोग किया जाता है return, तो उस फ़ंक्शन को कुछ विशेष में बदल दिया जाता है generator function। वह फ़ंक्शन generatorप्रकार का ऑब्जेक्ट लौटाएगा । कीवर्ड अजगर संकलक सूचित करने के लिए इस तरह के समारोह विशेष रूप से इलाज करने के लिए एक ध्वज है। सामान्य मूल्य समाप्त हो जाने के बाद सामान्य कार्य समाप्त हो जाएंगे। लेकिन कंपाइलर की मदद से, जनरेटर फ़ंक्शन को फिर से शुरू करने के बारे में सोचा जा सकता है । यही है, निष्पादन संदर्भ को बहाल किया जाएगा और निष्पादन अंतिम रन से जारी रहेगा। जब तक आप स्पष्ट रूप से रिटर्न नहीं कहते हैं, जो एक अपवाद को बढ़ाएगा (जो कि इटरेटर प्रोटोकॉल का हिस्सा भी है), या फ़ंक्शन के अंत तक पहुंच जाएगा। मुझे इस बारे में बहुत सारे संदर्भ मिलेyieldStopIterationgenerator एकसे functional programming perspectiveसबसे पाचक है।

(अब मैं तर्क के बारे में बात करना चाहता हूं generator, और iteratorअपनी समझ के आधार पर। मुझे आशा है कि यह आपको पुनरावृत्ति और जनरेटर की आवश्यक प्रेरणा को समझने में मदद कर सकता है । ऐसी अवधारणा अन्य भाषाओं में भी दिखाई देती है जैसे कि C #।)

जैसा कि मैं समझता हूं, जब हम डेटा का एक गुच्छा संसाधित करना चाहते हैं, तो हम आमतौर पर डेटा को पहले कहीं स्टोर करते हैं और फिर इसे एक-एक करके संसाधित करते हैं। लेकिन यह भोली दृष्टिकोण समस्यात्मक है। यदि डेटा वॉल्यूम बहुत बड़ा है, तो उन्हें पहले से ही संग्रहीत करना महंगा है। तो dataसीधे खुद को संग्रहीत करने के बजाय , किसी प्रकार का metadataअप्रत्यक्ष रूप से भंडारण क्यों न करें , अर्थातthe logic how the data is computed

ऐसे मेटाडेटा को लपेटने के लिए 2 दृष्टिकोण हैं।

  1. OO दृष्टिकोण, हम मेटाडेटा को लपेटते हैं as a class। यह तथाकथित है iteratorजो पुनरावृत्ति प्रोटोकॉल (यानी __next__(), और __iter__()विधियों) को लागू करता है । यह आमतौर पर देखा जाने वाला पुनरावृत्त डिजाइन पैटर्न भी है
  2. कार्यात्मक दृष्टिकोण, हम मेटाडेटा को लपेटते हैं as a function। यह तथाकथित है generator function। लेकिन हुड के तहत, फिर generator objectभी IS-Aपुनरावृत्त लौटा क्योंकि यह भी पुनरावृत्ति प्रोटोकॉल को लागू करता है।

किसी भी तरह से, एक इटरेटर बनाया जाता है, यानी कुछ ऑब्जेक्ट जो आपको इच्छित डेटा दे सकते हैं। OO दृष्टिकोण थोड़ा जटिल हो सकता है। वैसे भी, जो एक का उपयोग करने के लिए आप पर निर्भर है।


54

सारांश में, यह yieldकथन आपके कार्य को एक कारखाने में बदल देता है जो एक विशेष वस्तु का निर्माण करता है जिसे generatorआपके मूल कार्य के मुख्य भाग के चारों ओर लपेटता है। जब generatorइसे पुनरावृत्त किया जाता है, तो यह आपके फ़ंक्शन को तब तक निष्पादित करता है जब तक कि यह अगले तक नहीं पहुंच जाता है yieldतब निष्पादन को निलंबित कर देता है और पास किए गए मूल्य का मूल्यांकन करता है yield। यह प्रत्येक पुनरावृत्ति पर इस प्रक्रिया को दोहराता है जब तक कि निष्पादन का मार्ग फ़ंक्शन से बाहर नहीं निकलता है। उदाहरण के लिए,

def simple_generator():
    yield 'one'
    yield 'two'
    yield 'three'

for i in simple_generator():
    print i

बस आउटपुट

one
two
three

जनरेटर एक लूप के साथ जनरेटर का उपयोग करने से आता है जो एक अनुक्रम की गणना करता है, जनरेटर गणना के अगले परिणाम को 'उपज' के लिए हर बार रोकते हुए लूप को निष्पादित करता है, इस तरह से यह मक्खी पर एक सूची की गणना करता है, लाभ स्मृति होने के नाते विशेष रूप से बड़ी गणना के लिए बचाया

मान लें कि आप अपना स्वयं का rangeफ़ंक्शन बनाना चाहते हैं जो संख्याओं की पुनरावृत्ति रेंज का उत्पादन करता है, आप ऐसा कर सकते हैं,

def myRangeNaive(i):
    n = 0
    range = []
    while n < i:
        range.append(n)
        n = n + 1
    return range

और इसे इस तरह से उपयोग करें;

for i in myRangeNaive(10):
    print i

लेकिन यह अक्षम्य है क्योंकि

  • आप एक सरणी बनाते हैं जिसे आप केवल एक बार उपयोग करते हैं (यह मेमोरी बर्बाद करता है)
  • यह कोड वास्तव में उस सरणी पर दो बार लूप करता है! :(

सौभाग्य से गुइडो और उनकी टीम जनरेटर को विकसित करने के लिए पर्याप्त उदार थे इसलिए हम ऐसा कर सकते थे;

def myRangeSmart(i):
    n = 0
    while n < i:
       yield n
       n = n + 1
    return

for i in myRangeSmart(10):
    print i

अब प्रत्येक पुनरावृत्ति पर जनरेटर पर एक फ़ंक्शन कहा जाता है next()जब तक कि यह 'उपज' स्टेटमेंट तक नहीं पहुंचता है जिसमें यह रुक जाता है और मान को 'पैदावार' देता है या फ़ंक्शन के अंत तक पहुंचता है। पहले कॉल पर इस स्थिति में, next()उपज स्टेटमेंट तक पहुंच जाता है और 'एन' प्राप्त होता है, अगले कॉल पर यह वृद्धि स्टेटमेंट निष्पादित करेगा, 'जबकि' पर वापस जाएं, इसका मूल्यांकन करें, और यदि सही है, तो यह बंद हो जाएगा और उपज 'एन' फिर से, यह उस तरह से जारी रहेगा जब तक कि स्थिति झूठी नहीं हो जाती है और जनरेटर फ़ंक्शन के अंत तक कूदता है।


53

उपज एक वस्तु है

returnफ़ंक्शन में A एकल मान लौटाएगा।

यदि आप मानों का एक बड़ा सेट वापस करने के लिए फ़ंक्शन चाहते हैं , तो उपयोग करें yield

इससे भी महत्वपूर्ण बात, yieldएक बाधा है

CUDA भाषा में अवरोध की तरह, यह नियंत्रण को तब तक हस्तांतरित नहीं करेगा जब तक कि यह पूरा नहीं हो जाता।

यानी यह आपके फंक्शन में शुरू से लेकर हिट होने तक कोड को चलाएगा yield। फिर, यह लूप का पहला मान लौटाएगा।

फिर, हर दूसरी कॉल फंक्शन में आपके द्वारा लिखे गए लूप को एक और बार चलाएगी, अगले मूल्य को तब तक लौटाएगी जब तक कि लौटने का कोई मूल्य न हो।


52

बहुत से लोग returnइसके बजाय उपयोग करते हैं yield, लेकिन कुछ मामलों में yieldअधिक कुशल और काम करना आसान हो सकता है।

यहाँ एक उदाहरण है जो yieldनिश्चित रूप से सबसे अच्छा है:

वापसी (समारोह में)

import random

def return_dates():
    dates = [] # With 'return' you need to create a list then return it
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        dates.append(date)
    return dates

उपज (कार्य में)

def yield_dates():
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        yield date # 'yield' makes a generator automatically which works
                   # in a similar way. This is much more efficient.

कॉलिंग फ़ंक्शन

dates_list = return_dates()
print(dates_list)
for i in dates_list:
    print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
    print(i)

दोनों कार्य एक ही कार्य करते हैं, लेकिन yieldपांच के बजाय तीन लाइनों का उपयोग करता है और चिंता करने के लिए एक कम चर है।

यह कोड से परिणाम है:

उत्पादन

जैसा कि आप देख सकते हैं कि दोनों फ़ंक्शन समान कार्य करते हैं। एकमात्र अंतर return_dates()एक सूची yield_dates()देता है और एक जनरेटर देता है।

एक वास्तविक जीवन उदाहरण कुछ ऐसा होगा जैसे लाइन द्वारा फाइल लाइन पढ़ना या यदि आप सिर्फ एक जनरेटर बनाना चाहते हैं।


43

yieldएक समारोह के लिए एक वापसी तत्व की तरह है। अंतर यह है, कि yieldतत्व एक फ़ंक्शन को जनरेटर में बदल देता है। एक जनरेटर एक फ़ंक्शन की तरह व्यवहार करता है जब तक कि कुछ 'उपज' न हो। जनरेटर तब तक बंद हो जाता है जब तक कि इसे अगले बुलाया नहीं जाता है, और यह उसी बिंदु से जारी रहता है जब यह शुरू हुआ था। आप कॉल करके, एक में सभी 'उपज' मूल्यों का एक क्रम प्राप्त कर सकते हैं list(generator())



36

यहाँ एक सरल yieldआधारित दृष्टिकोण है, जो कि रिटरीज श्रृंखला की गणना करने के लिए समझाया गया है:

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

जब आप इसे अपने REPL में दर्ज करते हैं और फिर कोशिश करते हैं और इसे कॉल करते हैं, तो आपको एक रहस्यमय परिणाम मिलेगा:

>>> fib()
<generator object fib at 0x7fa38394e3b8>

ऐसा इसलिए है क्योंकि yieldपायथन को संकेत दिया गया है कि आप एक जनरेटर बनाना चाहते हैं , जो कि एक ऐसी वस्तु है जो मांग पर मूल्य उत्पन्न करती है।

तो, आप इन मूल्यों को कैसे उत्पन्न करते हैं? यह या तो सीधे अंतर्निहित फ़ंक्शन का उपयोग करके किया जा सकता हैnext , या, इसे अप्रत्यक्ष रूप से एक निर्माण के लिए खिलाकर जो मूल्यों का उपभोग करता है।

अंतर्निहित next()फ़ंक्शन का उपयोग करते हुए , आप सीधे जनरेटर का उपयोग करने के लिए मजबूर करते .next/ करते हैं __next__:

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

परोक्ष रूप से, अगर आप प्रदान करते हैं fibएक को forपाश, एक listप्रारंभकर्ता, एक tupleप्रारंभकर्ता, या कुछ और कि उम्मीद है एक वस्तु है कि उत्पन्न करता है / मूल्यों पैदा करता है, आप जनरेटर "उपभोग" करेंगे जब तक कोई और अधिक मूल्यों यह उत्पादन किया जा सकता है (और यह देता है) :

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

इसी तरह, एक tupleइनिशलाइज़र के साथ :

>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

एक जनरेटर एक फ़ंक्शन से इस अर्थ में भिन्न होता है कि यह आलसी है। यह इसे स्थानीय स्थिति बनाए रखने और जब भी आपको आवश्यकता हो, आपको फिर से शुरू करने की अनुमति देकर इसे पूरा करता है।

जब आप पहली बार fibइसे कॉल करके आते हैं:

f = fib()

पायथन फ़ंक्शन को संकलित करता है, yieldकीवर्ड का सामना करता है और बस आप पर एक जनरेटर ऑब्जेक्ट वापस करता है। बहुत उपयोगी नहीं लगता है।

जब आप अनुरोध करते हैं, तो यह पहला मूल्य उत्पन्न करता है, प्रत्यक्ष या अप्रत्यक्ष रूप से, यह उन सभी कथनों को निष्पादित करता है जो इसे पाता है, जब तक कि यह एक का सामना नहीं करता है, तब यह yieldआपके द्वारा आपूर्ति किए गए मूल्य को yieldरोक देता है और रोक देता है। एक उदाहरण के लिए जो इसे बेहतर प्रदर्शित करता है, आइए कुछ printकॉल का उपयोग करें ( print "text"यदि पायथन 2 पर है तो बदलें ):

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

अब, REPL में दर्ज करें:

>>> gen = yielder("Hello, yield!")

आपके पास एक जेनरेटर ऑब्जेक्ट है जो अब इसके लिए एक मूल्य उत्पन्न करने के लिए एक कमांड का इंतजार कर रहा है। उपयोग करें nextऔर देखें कि क्या मुद्रित है:

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

निर्विवाद परिणाम जो छपे हैं। उद्धृत परिणाम वही है जो इससे लौटा है yieldnextअब फिर से कॉल करें:

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

जनरेटर को यह याद है कि इसे वहां रोक दिया गया था yield valueऔर वहां से फिर से शुरू किया गया था। अगला संदेश मुद्रित होता है और उस yieldपर विराम देने के कथन की खोज फिर से ( whileलूप के कारण ) की जाती है।

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