पायथन में जनरेटर को समझना


218

मैं फिलहाल पायथन कुकबुक पढ़ रहा हूं और वर्तमान में जनरेटर देख रहा हूं। मुझे अपना सिर गोल करना मुश्किल लग रहा है।

जैसा कि मैं एक जावा बैकग्राउंड से आता हूं, क्या कोई जावा समकक्ष है? पुस्तक 'निर्माता / उपभोक्ता' के बारे में बोल रही थी, हालाँकि जब मैंने सुना कि मैं थ्रेडिंग के बारे में सोचता हूँ।

जनरेटर क्या है और आप इसका उपयोग क्यों करेंगे? किसी भी किताबों को उद्धृत किए बिना, जाहिर है (जब तक आप एक पुस्तक से एक सभ्य, सरल उत्तर नहीं पा सकते हैं)। शायद उदाहरण के साथ, अगर आप उदार महसूस कर रहे हैं!

जवाबों:


402

नोट: यह पोस्ट पायथन 3.x सिंटैक्स को मानता है।

एक जनरेटर केवल एक फ़ंक्शन है जो एक ऑब्जेक्ट देता है जिस पर आप कॉल कर सकते हैं next, जैसे कि प्रत्येक कॉल के लिए यह कुछ मूल्य देता है, जब तक कि यह एक StopIterationअपवाद नहीं उठाता है, यह दर्शाता है कि सभी मान उत्पन्न हुए हैं। ऐसी वस्तु को इटरेटर कहा जाता है

सामान्य फ़ंक्शंस returnजावा की तरह ही एक भी मान लौटाते हैं । हालांकि, पायथन में, एक विकल्प है, जिसे कहा जाता है yieldyieldकिसी फ़ंक्शन में कहीं भी उपयोग करने से यह एक जनरेटर बन जाता है। इस कोड को देखें:

>>> def myGen(n):
...     yield n
...     yield n + 1
... 
>>> g = myGen(6)
>>> next(g)
6
>>> next(g)
7
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

जैसा कि आप देख सकते हैं, myGen(n)एक फ़ंक्शन है जो पैदावार nऔर n + 1nextजब तक सभी मूल्यों की पैदावार नहीं हो जाती, तब तक हर कॉल एक मान देता है। पृष्ठभूमि में forलूप कॉल next, इस प्रकार:

>>> for n in myGen(6):
...     print(n)
... 
6
7

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

>>> g = (n for n in range(3, 5))
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

ध्यान दें कि जनरेटर अभिव्यक्तियाँ सूची बोध की तरह हैं :

>>> lc = [n for n in range(3, 5)]
>>> lc
[3, 4]

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

इस विषय में और भी बातें कही जानी हैं। यह sendएक जनरेटर ( संदर्भ ) में वापस डेटा के लिए संभव है । लेकिन यह एक ऐसी चीज है जो मैं आपको बताता हूं कि जब तक आप एक जनरेटर की मूल अवधारणा को नहीं समझते हैं, तब तक आप इसे नहीं देखते हैं।

अब आप पूछ सकते हैं: जनरेटर का उपयोग क्यों करें? कुछ अच्छे कारण हैं:

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

    >>> def fib():
    ...     a, b = 0, 1
    ...     while True:
    ...         yield a
    ...         a, b = b, a + b
    ... 
    >>> import itertools
    >>> list(itertools.islice(fib(), 10))
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    

    यह कोड itertools.isliceअनंत धारा से तत्वों की सीमित संख्या लेने के लिए उपयोग करता है। आपको itertoolsमॉड्यूल में कार्यों पर एक अच्छी नज़र रखने की सलाह दी जाती है , क्योंकि वे उन्नत जनरेटर को बड़ी आसानी से लिखने के लिए आवश्यक उपकरण हैं।


   पायथन के बारे में <= 2.6: उपरोक्त उदाहरणों nextमें एक फ़ंक्शन है जो __next__दिए गए ऑब्जेक्ट पर विधि को कॉल करता है । पायथन में <= 2.6 एक अलग तकनीक का उपयोग करता है, अर्थात् के o.next()बजाय next(o)। पायथन 2.7 में next()कॉल है .nextइसलिए आपको 2.7 में निम्नलिखित का उपयोग करने की आवश्यकता नहीं है:

>>> g = (n for n in range(3, 5))
>>> g.next()
3

9
आप उल्लेख करते हैं कि sendएक जनरेटर को डेटा देना संभव है । एक बार जब आप ऐसा करते हैं कि आपके पास एक 'कॉरआउट' है। कोराउटाइन के साथ उल्लेखित उपभोक्ता / निर्माता जैसे पैटर्न को लागू करना बहुत सरल है क्योंकि उन्हें एस की कोई आवश्यकता नहीं है Lockऔर इसलिए गतिरोध नहीं हो सकता है। धागे को कोसने के बिना कोरटाइन का वर्णन करना कठिन है, इसलिए मैं सिर्फ कहूंगा कि कोरआउट थ्रेडिंग का एक बहुत ही सुंदर विकल्प है।
जोचेन रिट्जेल

पायथन जनरेटर मूल रूप से ट्यूरिंग मशीनों के संदर्भ में हैं कि वे कैसे कार्य करते हैं?
उग्र फीनिक्स

48

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

>>> def myGenerator():
...     yield 'These'
...     yield 'words'
...     yield 'come'
...     yield 'one'
...     yield 'at'
...     yield 'a'
...     yield 'time'

>>> myGeneratorInstance = myGenerator()
>>> next(myGeneratorInstance)
These
>>> next(myGeneratorInstance)
words

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

>>> for word in myGeneratorInstance:
...     print word
These
words
come
one
at 
a 
time

ध्यान दें कि जनरेटर अनंतता से निपटने के लिए एक और तरीका प्रदान करते हैं, उदाहरण के लिए

>>> from time import gmtime, strftime
>>> def myGen():
...     while True:
...         yield strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())    
>>> myGeneratorInstance = myGen()
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:17:15 +0000
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:18:02 +0000   

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


30

सबसे पहले, शब्द जनरेटर मूल रूप से पाइथन में कुछ बीमार-परिभाषित था, जिससे बहुत भ्रम पैदा हुआ। आप शायद iterators और iterables मतलब है ( यहाँ देखें )। फिर पायथन में जेनरेटर फ़ंक्शंस (जो एक जेनरेटर ऑब्जेक्ट लौटाते हैं), जनरेटर ऑब्जेक्ट्स (जो पुनरावृत्तियाँ हैं) और जनरेटर एक्सप्रेशंस (जो एक जेनरेटर ऑब्जेक्ट के लिए मूल्यांकन किए जाते हैं) हैं।

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

यह अभी भी सटीक होने के लिए एक अच्छा विचार हो सकता है और आगे के विनिर्देश के बिना शब्द "जनरेटर" से बच सकता है।


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

22

जनरेटर बनाने के लिए जनरेटर के बारे में सोचा जा सकता है। वे एक जावा Iterator की तरह व्यवहार करते हैं। उदाहरण:

>>> g = (x for x in range(10))
>>> g
<generator object <genexpr> at 0x7fac1c1e6aa0>
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> list(g)   # force iterating the rest
[3, 4, 5, 6, 7, 8, 9]
>>> g.next()  # iterator is at the end; calling next again will throw
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

आशा है कि यह मदद करता है / आप के लिए क्या देख रहे हैं।

अपडेट करें:

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

>>> def infinite_gen():
...     n = 0
...     while True:
...         yield n
...         n = n + 1
... 
>>> g = infinite_gen()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
...

1
अब, Java ने Streams ले लिया है, जो कि जनरेटर के समान है, सिवाय इसके कि आप स्पष्ट रूप से बिना किसी आश्चर्यजनक राशि के अगले तत्व को प्राप्त नहीं कर सकते।
निधि मोनिका का मुकदमा

12

कोई जावा समकक्ष नहीं है।

यहाँ एक संक्षिप्त उदाहरण दिया गया है:

#! /usr/bin/python
def  mygen(n):
    x = 0
    while x < n:
        x = x + 1
        if x % 3 == 0:
            yield x

for a in mygen(100):
    print a

जनरेटर में एक लूप होता है जो 0 से n तक चलता है, और यदि लूप वेरिएबल 3 से अधिक है, तो यह वेरिएबल को पैदावार देता है।

forलूप के प्रत्येक पुनरावृत्ति के दौरान जनरेटर को निष्पादित किया जाता है। यदि यह पहली बार जनरेटर निष्पादित करता है, तो यह शुरुआत में शुरू होता है, अन्यथा यह पिछली बार से जारी रहता है जब इसकी उपज होती है।


2
अंतिम पैराग्राफ बहुत महत्वपूर्ण है: जनरेटर फ़ंक्शन की स्थिति हर बार 'स्थिर' हो जाती है, यह sth पैदावार देता है, और ठीक उसी स्थिति में जारी रहता है जब अगली बार इसे लागू किया जाता है।
जोहान्स चर्रा

जावा में "जेनरेटर एक्सप्रेशन" के लिए कोई सिंटैक्टिक समकक्ष नहीं है, लेकिन जनरेटर - एक बार जब आप एक हो जाते हैं - अनिवार्य रूप से सिर्फ एक इटरेटर (जावा इटरेटर के रूप में एक ही बुनियादी विशेषताओं)।
overthink

@overthink: खैर, जनरेटर के अन्य दुष्प्रभाव हो सकते हैं जो कि जावा पुनरावृत्तियों में नहीं हो सकते। यदि मुझे मेरे उदाहरण के print "hello"बाद रखा जाता है x=x+1, तो "हैलो" को 100 बार मुद्रित किया जाएगा, जबकि लूप के शरीर को अभी भी केवल 33 बार निष्पादित किया जाएगा।
वेर्न्से नोव

@ आईवर्नर: निश्चित रूप से जावा में समान प्रभाव हो सकता है। बराबर जावा इट्टर में अगले () का कार्यान्वयन अभी भी 0 से 99 (आपके मायजेन (100) उदाहरण का उपयोग करके) से खोजना होगा, इसलिए यदि आप चाहते हैं तो हर बार आप System.out.println () कर सकते हैं। आप अगले () से केवल 33 बार ही लौटेंगे। जावा की कमी बहुत आसान उपज सिंटैक्स है जो पढ़ने (और लिखने) में काफी आसान है।
overthink

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

8

मुझे स्टैक फ्रेम के संदर्भ में, प्रोग्रामिंग भाषाओं और कंप्यूटिंग में एक सभ्य पृष्ठभूमि वाले लोगों को जनरेटर का वर्णन करना पसंद है।

कई भाषाओं में, शीर्ष पर एक स्टैक होता है, जो वर्तमान स्टैक "फ्रेम" होता है। स्टैक फ़्रेम में फ़ंक्शन के लिए स्थानीय चर के लिए आवंटित स्थान शामिल है, जिसमें उस फ़ंक्शन को पारित किए गए तर्क शामिल हैं।

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

नियमित कार्यों के साथ, कुछ बिंदु पर फ़ंक्शन एक मान देता है, और स्टैक "पॉप्ड" होता है। फ़ंक्शन का स्टैक फ्रेम खारिज कर दिया जाता है और निष्पादन पिछले स्थान पर फिर से शुरू होता है।

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

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

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


6

केवल एक चीज जिसे मैं Stephan202 के उत्तर में जोड़ सकता हूं, यह अनुशंसा है कि आप डेविड बेज़ले की PyCon '08 प्रस्तुति "जेनरेटर ट्रिक्स फॉर सिस्टम प्रोग्रामर्स" पर एक नज़र डालें, जो जनरेटर के कैसे और क्यों का सबसे अच्छा एकल विवरण है जो मैंने देखा है। कहीं भी। यह वह चीज है जो मुझे "पायथन की तरह दिखती है" से "यह वही है जो मैं देख रहा हूं।" यह http://www.dabeaz.com/generators/ पर है


6

यह फ़ंक्शन फू और जनरेटर फू (n) के बीच एक स्पष्ट अंतर बनाने में मदद करता है:

def foo(n):
    yield n
    yield n+1

फू एक फंक्शन है। foo (6) एक जनरेटर वस्तु है।

जेनरेटर ऑब्जेक्ट का उपयोग करने का विशिष्ट तरीका एक लूप में है:

for n in foo(6):
    print(n)

लूप प्रिंट करता है

# 6
# 7

एक जनरेटर को फिर से शुरू करने वाले फ़ंक्शन के रूप में सोचें।

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

दृश्यों के पीछे, जब आप bar=foo(6)जनरेटर ऑब्जेक्ट बार कहते हैं तो आपके पास एक nextविशेषता है।

आप इसे फू से प्राप्त मूल्यों को पुनः प्राप्त करने के लिए खुद कह सकते हैं:

next(bar)    # Works in Python 2.6 or Python 3.x
bar.next()   # Works in Python 2.5+, but is deprecated. Use next() if possible.

जब फू समाप्त होता है (और अधिक next(bar)उपज वाले मूल्य नहीं होते हैं), तो कॉल स्टॉपइंटरनेशन त्रुटि होती है।


5

यह पोस्ट पायथन जनरेटर की उपयोगिता को समझाने के लिए एक उपकरण के रूप में फाइबोनैचि संख्याओं का उपयोग करेगा ।

इस पोस्ट में C ++ और पायथन कोड दोनों की सुविधा होगी।

फाइबोनैचि संख्याओं को अनुक्रम के रूप में परिभाषित किया गया है: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ...।

या सामान्य रूप में:

F0 = 0
F1 = 1
Fn = Fn-1 + Fn-2

इसे बेहद आसानी से C ++ फ़ंक्शन में स्थानांतरित किया जा सकता है:

size_t Fib(size_t n)
{
    //Fib(0) = 0
    if(n == 0)
        return 0;

    //Fib(1) = 1
    if(n == 1)
        return 1;

    //Fib(N) = Fib(N-2) + Fib(N-1)
    return Fib(n-2) + Fib(n-1);
}

लेकिन अगर आप पहले छह फाइबोनैचि संख्याओं को प्रिंट करना चाहते हैं, तो आप उपरोक्त फ़ंक्शन के साथ बहुत सारे मानों को पुन: संयोजित करेंगे।

उदाहरण के लिए: Fib(3) = Fib(2) + Fib(1)लेकिन Fib(2)यह भी पुनर्गणना करता है Fib(1)। जितना अधिक मूल्य आप गणना करना चाहते हैं, उतना ही खराब होगा।

तो किसी को राज्य के बारे में जानकारी देकर उपरोक्त को फिर से लिखने के लिए लुभाया जा सकता है main

// Not supported for the first two elements of Fib
size_t GetNextFib(size_t &pp, size_t &p)
{
    int result = pp + p;
    pp = p;
    p = result;
    return result;
}

int main(int argc, char *argv[])
{
    size_t pp = 0;
    size_t p = 1;
    std::cout << "0 " << "1 ";
    for(size_t i = 0; i <= 4; ++i)
    {
        size_t fibI = GetNextFib(pp, p);
        std::cout << fibI << " ";
    }
    return 0;
}

लेकिन यह बहुत बदसूरत है, और यह हमारे तर्क को उलझा देता है main। हमारे mainकार्य में राज्य के बारे में चिंता न करना बेहतर होगा ।

हम vectorमानों को वापस कर सकते हैं और मानों के iteratorउस सेट पर एक पुनरावृति का उपयोग कर सकते हैं , लेकिन इसके लिए बड़ी संख्या में रिटर्न मानों के लिए एक साथ सभी मेमोरी की आवश्यकता होती है।

तो अपने पुराने दृष्टिकोण पर वापस जाएं, यदि हम संख्याओं को प्रिंट करने के अलावा कुछ और करना चाहते हैं तो क्या होगा? हमें कोड के पूरे ब्लॉक को कॉपी और पेस्ट करना होगा और mainआउटपुट स्टेटमेंट को जो कुछ भी हम करना चाहते हैं उसे बदल सकते हैं। और यदि आप कोड को कॉपी और पेस्ट करते हैं, तो आपको गोली मार दी जानी चाहिए। आप गोली नहीं चलाना चाहते, क्या आप?

इन समस्याओं को हल करने के लिए, और शॉट लेने से बचने के लिए, हम कॉलबैक फ़ंक्शन का उपयोग करके कोड के इस ब्लॉक को फिर से लिख सकते हैं। जब भी कोई नया फाइबोनैचि नंबर आता है, हम कॉलबैक फ़ंक्शन को कॉल करेंगे।

void GetFibNumbers(size_t max, void(*FoundNewFibCallback)(size_t))
{
    if(max-- == 0) return;
    FoundNewFibCallback(0);
    if(max-- == 0) return;
    FoundNewFibCallback(1);

    size_t pp = 0;
    size_t p = 1;
    for(;;)
    {
        if(max-- == 0) return;
        int result = pp + p;
        pp = p;
        p = result;
        FoundNewFibCallback(result);
    }
}

void foundNewFib(size_t fibI)
{
    std::cout << fibI << " ";
}

int main(int argc, char *argv[])
{
    GetFibNumbers(6, foundNewFib);
    return 0;
}

यह स्पष्ट रूप से एक सुधार है, आपके तर्क में mainक्लॉट नहीं है, और आप फाइबोनैचि संख्याओं के साथ कुछ भी कर सकते हैं, बस नए कॉलबैक को परिभाषित करें।

लेकिन यह अभी भी सही नहीं है। क्या होगा यदि आप केवल पहले दो फाइबोनैचि संख्याएं प्राप्त करना चाहते थे, और फिर कुछ करें, फिर कुछ और प्राप्त करें, फिर कुछ और करें?

ठीक है, हम जा सकते हैं जैसे हम थे, और हम फिर से राज्य जोड़ना शुरू कर सकते हैं main, जिससे GetFibNumbers को एक मध्यस्थ बिंदु से शुरू किया जा सके। लेकिन यह हमारे कोड को और प्रस्फुटित करेगा, और यह पहले से ही फाइबोनैचि संख्याओं को प्रिंट करने जैसे सरल कार्य के लिए बहुत बड़ा है।

हम धागे के एक जोड़े के माध्यम से एक निर्माता और उपभोक्ता मॉडल को लागू कर सकते हैं। लेकिन इससे कोड और भी जटिल हो जाता है।

इसके बजाय चलो जनरेटर के बारे में बात करते हैं।

पायथन में एक बहुत अच्छी भाषा की सुविधा है जो इन जेनरेटर नामक समस्याओं को हल करती है।

एक जनरेटर आपको एक फ़ंक्शन निष्पादित करने, एक मनमाना बिंदु पर रुकने, और फिर फिर से जारी रखने की अनुमति देता है जहां आपने छोड़ा था। हर बार एक मान लौटाते हुए।

जनरेटर का उपयोग करने वाले निम्नलिखित कोड पर विचार करें:

def fib():
    pp, p = 0, 1
    while 1:
        yield pp
        pp, p = p, pp+p

g = fib()
for i in range(6):
    g.next()

जो हमें परिणाम देता है:

0 1 1 2 3 5

yieldबयान अजगर जनरेटर के साथ conjuction में प्रयोग किया जाता है। यह फंक्शन की स्थिति को बचाता है और फील किया हुआ मान लौटाता है। अगली बार जब आप जनरेटर पर अगले () फ़ंक्शन को कॉल करते हैं, तो यह जारी रहेगा जहां उपज छोड़ दिया गया है।

यह कॉलबैक फ़ंक्शन कोड की तुलना में कहीं अधिक साफ है। हमारे पास क्लीनर कोड, छोटा कोड है, और अधिक कार्यात्मक कोड का उल्लेख नहीं करना है (पायथन मनमाने ढंग से बड़े पूर्णांक की अनुमति देता है)।

स्रोत


3

मेरा मानना ​​है कि लगभग 20 साल पहले आईकैट प्रोग्रामिंग भाषा में पुनरावृत्तियों और जनरेटर की पहली उपस्थिति थी।

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

वहां बस कुछ पैराग्राफ पढ़ने के बाद, जनरेटर और पुनरावृत्तियों की उपयोगिता अधिक स्पष्ट हो सकती है।


2

सूची की समझ के साथ अनुभव ने पूरे पायथन में उनकी व्यापक उपयोगिता को दर्शाया है। हालाँकि, उपयोग के कई मामलों में मेमोरी में पूरी सूची बनाने की आवश्यकता नहीं होती है। इसके बजाय, उन्हें केवल एक समय में तत्वों पर पुनरावृति करने की आवश्यकता होती है।

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

sum([x*x for x in range(10)])

इसके बजाय जनरेटर अभिव्यक्ति का उपयोग करके मेमोरी को संरक्षित किया जाता है:

sum(x*x for x in range(10))

कंटेनर वस्तुओं के लिए कंस्ट्रक्टरों पर समान लाभ दिए गए हैं:

s = Set(word  for line in page  for word in line.split())
d = dict( (k, func(k)) for k in keylist)

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

max(len(line)  for line in file  if line.strip())

अधिक


1

मैंने कोड के इस टुकड़े को रखा जिसमें जनरेटर के बारे में 3 मुख्य अवधारणाएँ बताई गई हैं:

def numbers():
    for i in range(10):
            yield i

gen = numbers() #this line only returns a generator object, it does not run the code defined inside numbers

for i in gen: #we iterate over the generator and the values are printed
    print(i)

#the generator is now empty

for i in gen: #so this for block does not print anything
    print(i)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.