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




15 Answers

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() विधि के अंदर कोड बहुत जटिल लग रहा है और बग के लिए प्रवण हो सकता है। यहां जेनरेटर एक स्वच्छ और आसान समाधान प्रदान करते हैं।

python iterator generator yield coroutine

पायथन में 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




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एक थिटरेटर को कॉल होने तक कॉल करता है। आपके द्वारा पोस्ट किए गए कोड नमूने के मामले में, यह केवल एक टुपल लौटने और सूची में संलग्न करने के लिए बहुत स्पष्ट होगा।




उन लोगों के लिए जो कम से कम कामकाजी उदाहरण पसंद करते हैं, इस इंटरैक्टिव 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 squares_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

यह करो:

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

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

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

yield कहने का एक शर्करा तरीका है

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

वही व्यवहार:

>>> for square in squares_list(4):
...     print(square)
...
0
1
4
9
>>> for square in squares_the_yield_way(4):
...     print(square)
...
0
1
4
9

अलग व्यवहार:

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

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

यील्ड बहुमुखी है । यह अनंत लूप कर सकता है:

>>> 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(squares_the_yield_way(4))
[0, 1, 4, 9]

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

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

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

उपज - रास्ता दें या छोड़ दें (राजनीतिक शक्ति में)

... इटेटरेटर अग्रिम तक CPU निष्पादन को छोड़ दें।




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

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कीवर्ड के उपयोग के बिना जेनरेटर ऑब्जेक्ट्स को लागू करने के लिए निरंतरता उत्तीर्ण शैली का उपयोग करने का प्रयास करें ।




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

yieldनिम्नलिखित कोड में क्या करता है यह समझने में सहायता के लिए, आप किसी भी कोड के माध्यम से चक्र का पता लगाने के लिए अपनी अंगुली का उपयोग कर सकते हैं yield। हर बार जब आपकी उंगली हिट करती है yield, तो आपको ए nextया ए sendदर्ज करने की प्रतीक्षा करनी होगी। जब किसी nextको बुलाया जाता है, तब तक आप कोड के माध्यम से पता लगाते हैं जब तक कि आप हिट नहीं करते yield... yieldमूल्यांकन के दाईं ओर कोड का मूल्यांकन किया जाता है और कॉलर पर वापस आ जाता है ... तो आप प्रतीक्षा करते हैं। जब nextफिर से कहा जाता है, तो आप कोड के माध्यम से एक और पाश प्रदर्शन करते हैं। हालांकि, आप ध्यान देंगे कि एक कोरआउटिन में 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()



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

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

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




प्रोग्रामिंग दृष्टिकोण से, इटरेटर को 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
->



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

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अनुक्रम जनरेटर बनाने के लिए प्रयोग किया जाता है। इसका उपयोग गतिशील रूप से कुछ अनुक्रम उत्पन्न करने के लिए किया जाता है। उदाहरण के लिए, नेटवर्क पर लाइन द्वारा फ़ाइल लाइन पढ़ने के दौरान, आप 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"



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




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

जब एक अजगर समारोह में एक के 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 +=




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

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

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

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

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




Related