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




iterator generator (20)

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

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

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

जेनरेटर:

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

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

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

>>> 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)

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

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

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

>>> 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

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

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

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

>>> 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__ विधि है जो एक __iter__ है । एक पुनरावर्तक एक .next (पायथन 2 या .__next__ (पायथन 3) विधि प्रदान करता है, जिसे स्पष्ट रूप से लूप के for बुलाया जाता है जब तक कि यह StopIteration नहीं बढ़ाता है, और एक बार ऐसा करने पर, ऐसा करना जारी रहेगा।
  • यह जनरेटर क्या है इसका वर्णन करने के लिए जनरेटर अभिव्यक्ति का उपयोग करता है। चूंकि जनरेटर एक इटरेटर बनाने का एक सुविधाजनक तरीका है , यह केवल इस मामले को भ्रमित करता है, और हम अभी भी yield भाग तक नहीं पहुंच पाए हैं।
  • जेनरेटर थकावट को नियंत्रित करने में वह .next विधि को कॉल करता है, जब इसके बजाय उसे .next फ़ंक्शन का उपयोग करना चाहिए, तो next । यह संकेत की एक उचित परत होगी, क्योंकि उसका कोड पायथन 3 में काम नहीं करता है।
  • Itertools? यह yield लिए प्रासंगिक नहीं था।
  • पाइथन 3 में नई कार्यक्षमता yield from के साथ प्रदान की जाने वाली विधियों की कोई चर्चा नहीं । शीर्ष / स्वीकृत उत्तर एक बहुत ही अपूर्ण उत्तर है।

उत्तर की आलोचना जनरेटर अभिव्यक्ति या समझ में yield का सुझाव 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

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

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

30 जनवरी 2017 को 1 9:05 को ब्रेट कैनन ने लिखा:

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

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

मेरा वोट यह एक सिंटेक्स त्रुटि है क्योंकि आप सिंटैक्स से जो अपेक्षा करते हैं उसे प्राप्त नहीं कर रहे हैं।

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

वहां पहुंचने के मामले में, हम शायद चाहेंगे:

  • 3.7 में सिंटेक्स चेतावनी या गिरावट चेतावनी
  • 2.7.x में Py3k चेतावनी
  • 3.8 में सिंटेक्स त्रुटि

चीयर्स, निक।

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

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

नीचे की रेखा, जब तक कि सीपीथन के डेवलपर्स हमें अन्यथा न बताएं: जनरेटर अभिव्यक्ति या समझ में न डालें 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 यह परिवर्तन किसी उपयोगकर्ता को जनरेटर में डेटा भेजने की अनुमति देने के लिए प्रस्तावित किया गया था जैसा कि कोई इसे प्राप्त कर सकता है। डेटा भेजने के लिए, कोई इसे किसी चीज़ को असाइन करने में सक्षम होना चाहिए, और उसके लिए, एक कथन बस काम नहीं करेगा।

https://code.i-harness.com

पायथन में 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) से आता है, जिन्होंने मीट्रिक रिक्त स्थान के लिए एक महान पायथन पुस्तकालय बनाया है। यह संपूर्ण स्रोत का लिंक है: मॉड्यूल mspace


Grokking yield लिए शॉर्टकट

जब आप yield बयान के साथ एक समारोह देखते हैं, तो यह समझने के लिए यह आसान चाल लागू करें कि क्या होगा:

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

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

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

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

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

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

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

    कॉलर iter(mylist) कॉल करें -> यह एक ऑब्जेक्ट को next() विधि (या __next__() पायथन 3 में देता है)।

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

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

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

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

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

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

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

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

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

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

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

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

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


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

फिर, जनरेटर का उपयोग करने के for प्रत्येक बार आपका कोड चलाया जाएगा।

अब कठिन हिस्सा:

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

फ़ंक्शन चलाने के बाद जेनरेटर खाली माना जाता है, लेकिन अब 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 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 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 candidates 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() विधि एक सूची ऑब्जेक्ट विधि है जो एक पुनरावर्तनीय की अपेक्षा करती है और इसकी मान सूची में जोड़ती है।

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

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

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

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

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

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

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

>>> 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(corner_street_atm.__next__()) या print(next(corner_street_atm))

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

Itertools, आपका सबसे अच्छा दोस्त

Itertools मॉड्यूल में iterables में हेरफेर करने के लिए विशेष कार्य होते हैं। कभी जनरेटर को डुप्लिकेट करना चाहते हैं? दो जेनरेटर चेन? एक नेस्टर सूची में समूह मूल्य एक लाइनर के साथ? एक और सूची बनाने के बिना 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 एक प्रक्रिया है जो __iter__() लागू करती है ( __iter__() विधि को कार्यान्वित करती है) और iterators ( __next__() विधि को कार्यान्वित करना)। Iterables कोई भी वस्तुएं हैं जिन्हें आप एक इटरेटर प्राप्त कर सकते हैं। Iterators वे ऑब्जेक्ट्स हैं जो आपको पुनरावृत्तियों पर फिर से चलने देते हैं।

लूप काम करने के तरीके for बारे में इस लेख में इसके बारे में और कुछ है।


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

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

संक्षेप में: जनरेटर एक आलसी, वृद्धिशील लंबित सूची है , और 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 की range । कॉलिंग makeRange(n) एक जनरेटर 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 बजाय chain.from_iterable का उपयोग करना सुनिश्चित करें)। उदाहरण के लिए, आप जेनरेटर का उपयोग असीमित-लंबी आलसी सूचियों जैसे itertools.count() को लागू करने के लिए भी कर सकते हैं। आप अपने स्वयं के def enumerate(iterable): zip(count(), iterable) को कार्यान्वित कर सकते हैं def enumerate(iterable): zip(count(), iterable) , या वैकल्पिक रूप से थोड़ी देर में yield कीवर्ड के साथ ऐसा करते हैं।

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

परदे के पीछे

इस तरह "पायथन पुनरावृत्ति प्रोटोकॉल" काम करता है। यही है, जब आप list(makeRange(5)) करते हैं तो क्या हो रहा है 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() फ़ंक्शन (पुनरावृत्ति प्रोटोकॉल के अन्य हिस्सों 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 कर रहे हैं) पूरी तरह जरूरी है, तो itertools.tee उपयोग कर सकते हैं, क्योंकि itertools.tee करने itertools.tee इटरेटर पायथन PEP मानकों का प्रस्ताव स्थगित कर दिया गया है।


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

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

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


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

एक returnसमारोह में एक एकल मूल्य वापस करेगा।

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

सबसे महत्वपूर्ण बात yieldयह है कि एक बाधा है

सीयूडीए भाषा में बाधा की तरह, यह पूरा होने तक नियंत्रण स्थानांतरित नहीं करेगा।

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

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


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

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

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)

फिर भी एक और टीएल; डीआर

सूची में इटरेटर : सूचीnext() का अगला तत्व देता है

इटरेटर जनरेटर : next()फ्लाई पर अगले तत्व की गणना करेगा (कोड निष्पादित करें)

आप उपज / जेनरेटर को बाहर से नियंत्रण प्रवाह को मैन्युअल रूप से चलाने के तरीके के रूप में देख सकते हैं (जैसे लूप एक चरण जारी रखें), कॉल करके next, हालांकि जटिल जटिल।

नोट : जेनरेटर सामान्य कार्य नहीं है। यह पिछले राज्य को स्थानीय चर (ढेर) जैसे याद करता है। विस्तृत स्पष्टीकरण के लिए अन्य उत्तरों या लेख देखें। जनरेटर केवल एक बार फिर से किया जा सकता है । आप बिना कर सकते हैं yield, लेकिन यह उतना अच्छा नहीं होगा, इसलिए इसे 'बहुत अच्छी' भाषा चीनी माना जा सकता है।


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

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 मिलियन की सूची चाहते हैं तो क्या होगा? न केवल यह एक बड़ी स्मृति खाने वाला है, बल्कि उस समारोह को बनाने के लिए बहुत समय लगता है जब समारोह कहा जाता है। दूसरे मामले में, बार आपको सिर्फ जनरेटर देता है। एक जनरेटर एक पुनरावृत्ति है - जिसका अर्थ है कि आप इसे लूप, आदि में उपयोग कर सकते हैं, लेकिन प्रत्येक मान को केवल एक बार एक्सेस किया जा सकता है। सभी मान एक ही समय में स्मृति में संग्रहीत नहीं होते हैं; जेनरेटर ऑब्जेक्ट "याद करता है" जहां यह आखिरी बार आपको लूपिंग में था - इस तरह, यदि आप 50 अरब तक गिनने के लिए एक अचूक उपयोग कर रहे हैं, तो आपको 50 बिलियन तक गिनना नहीं है एक बार में और 50 अरब संख्याओं को गिनने के लिए स्टोर करें। फिर, यह एक सुंदर योगदान उदाहरण है,यदि आप वास्तव में 50 अरब तक गिनना चाहते हैं तो आप शायद itertools का उपयोग करेंगे। :)

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


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

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प्रोग्राम प्रवाह की स्थिति है और अगला लूप "उपज" स्थिति से शुरू होता है। ऐसा लगता है कि यह उस स्थिति की प्रतीक्षा कर रहा है, और उसके ठीक पहले, बाहर एक मूल्य लौट रहा है, और अगली बार काम जारी है।

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


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

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

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

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

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

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

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


yieldकीवर्ड बस लौटने परिणाम एकत्र करता है। yieldपसंद के बारे में सोचोreturn +=


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

>>> 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

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

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);

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


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

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कहा जाता है, यह समारोह को निरंतरता वापस करने के लिए कहता है। जब फ़ंक्शन को फिर से कॉल किया जाता है, तो यह जहां से छोड़ा जाता है वहां से शुरू होता है। तो, छद्म-छद्म कोड (यानी, छद्म कोड नहीं, लेकिन कोड नहीं) जनरेटर की 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कीवर्ड के उपयोग के बिना जेनरेटर ऑब्जेक्ट्स को लागू करने के लिए निरंतरता उत्तीर्ण शैली का उपयोग करने का प्रयास करें ।


कई लोग 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()देता है और जनरेटर देता है।

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


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

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

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

for line in getNextLines():
    doSomeThing(line)

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

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

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

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"

प्रोग्रामिंग दृष्टिकोण से, इटरेटर को thunks रूप में कार्यान्वित किया thunks

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

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

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

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

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

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
->

यहां क्या 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(..), हम पुष्टि कर सकते हैं कि ऊपर हमारा मानसिक मॉडल विश्वसनीय है।


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

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

for i in simple_generator():
    print i

बस outputs

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()उपज कथन तक निष्पादित होता है और अगली कॉल पर 'एन' उपज करता है, यह वृद्धि विवरण को निष्पादित करेगा, 'समय' पर वापस कूदें, इसका मूल्यांकन करें, और यदि सही है, तो यह रुक जाएगा और उपज 'एन' फिर से, यह तब तक जारी रहेगा जब तक कि स्थिति शर्त कम न हो और जनरेटर फ़ंक्शन के अंत तक कूद जाए।





coroutine