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


इसका उपयोग क्या है 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


8325
2017-10-23 22:21


मूल




जवाब:


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

Iterables

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

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

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

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

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

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

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


12189
2017-10-23 22:48



सब इटेटर को केवल एक बार फिर से सक्रिय किया जा सकता है, न केवल जनरेटर कार्यों द्वारा उत्पादित। यदि आप मुझ पर विश्वास नहीं करते हैं, तो कॉल करें iter() किसी भी अस्थिर वस्तु पर और परिणाम से अधिक बार फिर से प्रयास करने की कोशिश करें। - augurar
@Craicerjack आपके पास मिश्रित शब्द हैं। एक पुनरावृत्ति एक के साथ कुछ है __iter__ तरीका। एक पुनरावर्तक कॉलिंग का परिणाम है iter() एक पुनरावर्तनीय पर। Iterators केवल एक बार में फिर से किया जा सकता है। - augurar
yield यह जवाब जादुई नहीं है यह जवाब बताता है। जब आप एक फ़ंक्शन को कॉल करते हैं जिसमें एक होता है yield कहीं भी कथन, आपको जनरेटर ऑब्जेक्ट मिलता है, लेकिन कोई कोड नहीं चलता है। फिर प्रत्येक बार जब आप जेनरेटर से ऑब्जेक्ट निकालते हैं, तो पायथन फ़ंक्शन में कोड निष्पादित करता है जब तक कि यह कोई नहीं आता है yield कथन, फिर ऑब्जेक्ट को रोकता है और वितरित करता है। जब आप एक और वस्तु निकालते हैं, तो पाइथन बस के बाद फिर से शुरू होता है yield और जब तक यह दूसरे तक नहीं पहुंच जाता तब तक जारी रहता है yield (अक्सर वही एक, लेकिन बाद में एक पुनरावृत्ति)। यह तब तक जारी रहता है जब तक कि फ़ंक्शन अंत तक नहीं चलता है, जिस बिंदु पर जेनरेटर थक जाता है। - Matthias Fripp
@MatthiasFripp ने बिल्कुल बताया है कि मुझे इस जवाब को पढ़ने में क्या ठोकर खाई है। "आपका कोड हर बार चलाया जाएगा" एक भ्रामक बयान है। "आपका कोड कहां से छोड़ा जाएगा, इसे जारी रखने के लिए एक सटीक तरीका है"। - Indigenuity
"ये पुनरावृत्तियों आसान हैं ... लेकिन आप सभी मानों को स्मृति में संग्रहीत करते हैं और यह हमेशा आप जो चाहते हैं वह हमेशा नहीं होता", या तो गलत या भ्रमित है। एक पुनरावर्तनीय पुनरावर्तक पर iter () को कॉल करने पर एक पुनरावर्तक लौटाता है, और एक पुनरावर्तक को हमेशा इसके मानों को स्मृति में संग्रहीत नहीं करना पड़ता है, इसके कार्यान्वयन के आधार पर आईटीईआर विधि, यह मांग पर अनुक्रम में मूल्य भी उत्पन्न कर सकता है। - picmate 涅


शॉर्टकट Grokking  yield

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

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

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

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

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

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

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

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

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

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


1638
2017-10-25 21:22



"जब आप उपज बयान के साथ एक समारोह देखते हैं, तो यह समझने के लिए यह आसान चाल लागू करें कि क्या होगा" क्या यह इस तथ्य को पूरी तरह से अनदेखा नहीं करता है कि आप कर सकते हैं send जनरेटर में, जो जेनरेटर के बिंदु का एक बड़ा हिस्सा है? - DanielSank
"यह लूप के लिए हो सकता है, लेकिन यह कोड भी हो सकता है otherlist.extend(mylist)"-> यह गलत है। extend() सूची में सूची को संशोधित करता है और एक पुनरावर्तनीय वापस नहीं करता है। लूप करने की कोशिश कर रहा है otherlist.extend(mylist) एक के साथ असफल हो जाएगा TypeError इसलिये extend() निस्संदेह रिटर्न None, और आप लूप नहीं कर सकते हैं None। - Pedro
@pedro आपने उस वाक्य को गलत समझा है। इसका मतलब है कि पायथन दो उल्लिखित चरणों को करता है mylist (पर नहीं otherlist) निष्पादित करते समय otherlist.extend(mylist)। - today


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

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

मूल संस्करण:

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

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

मुझे यह ध्यान रखना चाहिए है चित्रकारी उद्देश्यों के लिए एक oversimplification। :)


397
2017-10-23 22:28



__getitem__ इसके बजाय परिभाषित किया जा सकता है __iter__। उदाहरण के लिए: class it: pass; it.__getitem__ = lambda self, i: i*10 if i < 10 else [][0]; for i in it(): print(i), यह प्रिंट करेगा: 0, 10, 20, ..., 9 0 - jfs
मैंने पाइथन 3.6 में इस उदाहरण की कोशिश की और यदि मैं बनाउंगा iterator = some_function(), परिवर्तनीय iterator एक समारोह नहीं है next() अब और केवल एक __next__() समारोह। सोचा था कि मैं इसका जिक्र करूंगा। - Peter


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

  1. अगर संकलक का पता लगाता है yield कीवर्ड कहीं भी एक समारोह के अंदर, यह फ़ंक्शन अब के माध्यम से वापस नहीं आता है return बयान। बजाय, यह हाथोंहाथ एक देता है आलसी "लंबित सूची" वस्तु जनरेटर कहा जाता है
  2. एक जनरेटर पुनरावर्तनीय है। एक क्या है iterable? यह एक जैसा है 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। कॉलिंग 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एक समय-लूप में कीवर्ड।

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


परदे के पीछे

इस तरह "पायथन पुनरावृत्ति प्रोटोकॉल" काम करता है। यही है, जब आप करते हैं तो क्या हो रहा है 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() समारोह (और पुनरावृत्ति प्रोटोकॉल के अन्य हिस्सों) फैंसी चीजों को लागू करने के लिए, आमतौर पर पठनीयता की कीमत पर, इसलिए ऐसा करने से बचने की कोशिश करें ...


ज़रा सी बात

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

पायथन में बोलो, ए iterable एक ऐसी वस्तु है जो एक सूची की तरह "फॉर-लूप की अवधारणा को समझती है" [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 अगर पूरी तरह जरूरी है, तो कॉपी करने योग्य इटरेटर पायथन पीईपी मानक प्रस्ताव स्थगित कर दिया गया है।


348
2018-06-19 06:33





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

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

  • के साथ एक समारोह yield, जब बुलाया जाता है, एक देता है जनक
  • जनरेटर इटरेटर हैं क्योंकि वे लागू करते हैं इटरेटर प्रोटोकॉल, तो आप उन पर फिर से कर सकते हैं।
  • एक जनरेटर भी हो सकता है भेजी गई जानकारी, इसे अवधारणात्मक रूप से बनाते हैं coroutine
  • पायथन 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)

सबसे पहले, हमें जेनरेटर को बिल्टिन फ़ंक्शन के साथ कतारबद्ध करना होगा, 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 Python 3 में उपलब्ध है। यह हमें प्रतिनिधि करने की अनुमति देता है एक subcoroutine के लिए coroutines:

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

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

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

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 बहुत कुछ करता है मुझे यकीन है कि मैं और भी जोड़ सकता हूं इस के लिए पूरी तरह से उदाहरण। यदि आप अधिक चाहते हैं या कुछ रचनात्मक आलोचना है, तो मुझे टिप्पणी करके मुझे बताएं नीचे।


अनुबंध:

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

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

उत्तर देने की आलोचना का सुझाव 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 में हैं       अच्छा नहीं है, आईएमएचओ।

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

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

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

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


254
2018-06-25 06:11





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

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

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


230
2017-10-23 22:24



यह करीब है, लेकिन सही नहीं है। हर बार जब आप इसमें उपज स्टेटमेंट के साथ एक फ़ंक्शन को कॉल करते हैं, तो यह एक ब्रांड नई जनरेटर ऑब्जेक्ट देता है। यह तभी होता है जब आप उस जेनरेटर की .next () विधि को कॉल करते हैं जो अंतिम उपज के बाद निष्पादन फिर से शुरू होता है। - kurosch
ऐसा लगता है कि कुछ गायब है "अगली बार समारोह फिर से शुरू होगा"। क्या यह होना चाहिए "अगली बार समारोह फिर से शुरू होगा रन"? - Peter Mortensen


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

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

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


182
2017-10-24 08:44





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

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

155
2018-01-18 17:25



यह सवाल का जवाब नहीं देता है - ppperry


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

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 अरब तक गिनना चाहते हैं तो आप शायद इटरटोल का उपयोग करेंगे। :)

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


133
2018-01-16 06:42





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

वहाँ एक है आईबीएम लेख जो इसे स्पष्ट रूप से अच्छी तरह से बताता है (पायथन के लिए) जहां तक ​​मैं देख सकता हूं।

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


125
2017-10-23 22:26