जवाबों:
जनरेटर आपको आलसी मूल्यांकन देते हैं। आप उन पर पुनरावृत्ति करके उनका उपयोग करते हैं, या तो स्पष्ट रूप से 'के लिए' के साथ या किसी भी फ़ंक्शन को पास करके या उस आंकड़े का निर्माण करके। आप कई वस्तुओं को वापस करने के रूप में जनरेटर के बारे में सोच सकते हैं, जैसे कि वे एक सूची लौटाते हैं, लेकिन एक बार में उन सभी को एक-एक करके वापस करने के बजाय, और अगले फ़ंक्शन का अनुरोध होने तक जनरेटर फ़ंक्शन को रोक दिया जाता है।
जेनरेटर परिणामों के बड़े सेटों की गणना करने के लिए अच्छे हैं (विशेष रूप से स्वयं को शामिल करने वाली गणनाओं में) जहां आप नहीं जानते कि क्या आपको सभी परिणामों की आवश्यकता है, या जहां आप एक ही समय में सभी परिणामों के लिए मेमोरी आवंटित नहीं करना चाहते हैं । या ऐसी स्थितियों के लिए जहां जनरेटर किसी अन्य जनरेटर का उपयोग करता है , या किसी अन्य संसाधन का उपभोग करता है, और यह अधिक सुविधाजनक है यदि ऐसा हुआ है जितनी देर हो सके।
जनरेटर के लिए एक और उपयोग (जो वास्तव में एक ही है) पुनरावृत्ति के साथ कॉलबैक को बदलने के लिए है। कुछ स्थितियों में आप बहुत सारे काम करना चाहते हैं और कभी-कभी फोन करने वाले को वापस रिपोर्ट करते हैं। परंपरागत रूप से आप इसके लिए कॉलबैक फ़ंक्शन का उपयोग करेंगे। आप इस कॉलबैक को कार्य-कार्य में पास करते हैं और यह समय-समय पर इस कॉलबैक को कॉल करेगा। जनरेटर दृष्टिकोण यह है कि कार्य-फ़ंक्शन (अब एक जनरेटर) कॉलबैक के बारे में कुछ भी नहीं जानता है, और जब भी यह कुछ रिपोर्ट करना चाहता है, तो केवल उपज देता है। कॉल करने वाला, एक अलग कॉलबैक लिखने और काम-फ़ंक्शन को पास करने के बजाय, जनरेटर के चारों ओर 'लूप' के लिए सभी रिपोर्टिंग काम करता है।
उदाहरण के लिए, मान लें कि आपने 'फाइलसिस्टम सर्च' प्रोग्राम लिखा है। आप खोज को उसकी संपूर्णता में कर सकते हैं, परिणाम एकत्र कर सकते हैं और फिर उन्हें एक बार में प्रदर्शित कर सकते हैं। पहले दिखाए जाने से पहले सभी परिणामों को एकत्रित करना होगा, और सभी परिणाम एक ही समय में मेमोरी में होंगे। या आप उन्हें खोजने के दौरान परिणाम प्रदर्शित कर सकते हैं, जो कि अधिक स्मृति कुशल और उपयोगकर्ता के प्रति अधिक मित्रता होगी। बाद के परिणाम को प्रिंटिंग-फंक्शन को फाइलसिस्टम-सर्च फंक्शन में पास किया जा सकता है, या यह केवल सर्च फंक्शन को एक जनरेटर बनाकर और परिणाम पर पुनरावृति करके किया जा सकता है।
यदि आप बाद के दो दृष्टिकोणों का उदाहरण देखना चाहते हैं, तो os.path.walk () (कॉलबैक के साथ पुराना फाइल सिस्टम-वॉकिंग फंक्शन) और os.walk () (नया फाइलसिस्टम-वॉकिंग जनरेटर) देखें। आप वास्तव में एक सूची में सभी परिणामों को इकट्ठा करना चाहते थे, जेनरेटर दृष्टिकोण बड़ी सूची के दृष्टिकोण में बदलने के लिए तुच्छ है:
big_list = list(the_generator)
yield
और join
उनके बाद अगले परिणाम प्राप्त करने के लिए मुसीबत में जाता है , यह समानांतर में निष्पादित नहीं करता है (और कोई मानक पुस्तकालय जनरेटर ऐसा नहीं करता है; चुपके से थ्रेड लॉन्च करना) पर फेंक दिया जाता है। जनरेटर yield
अगले मूल्य का अनुरोध किए जाने तक प्रत्येक पर रुकता है। यदि जेनरेटर I / O को लपेट रहा है, तो OS इस अनुमान पर फ़ाइल से डेटा को कैशिंग कर सकता है कि इसे शीघ्र ही अनुरोध किया जाएगा, लेकिन यह OS, Python शामिल नहीं है।
जनरेटर का उपयोग करने का एक कारण कुछ प्रकार के समाधानों के लिए समाधान को स्पष्ट करना है।
दूसरा एक समय में परिणामों का इलाज करना है, परिणामों की विशाल सूचियों के निर्माण से परहेज करें जिन्हें आप वैसे भी अलग करेंगे।
यदि आपके पास इस तरह का एक फंक्शन अप-टू-एन-एन है:
# function version
def fibon(n):
a = b = 1
result = []
for i in xrange(n):
result.append(a)
a, b = b, a + b
return result
आप अधिक आसानी से इस प्रकार फ़ंक्शन लिख सकते हैं:
# generator version
def fibon(n):
a = b = 1
for i in xrange(n):
yield a
a, b = b, a + b
फ़ंक्शन स्पष्ट है। और यदि आप फ़ंक्शन का उपयोग इस तरह से करते हैं:
for x in fibon(1000000):
print x,
इस उदाहरण में, यदि जनरेटर संस्करण का उपयोग करते हैं, तो पूरी 1000000 आइटम सूची बिल्कुल भी नहीं बनाई जाएगी, एक समय में केवल एक मूल्य। सूची संस्करण का उपयोग करते समय ऐसा नहीं होगा, जहां पहले एक सूची बनाई जाएगी।
list(fibon(5))
PEP 255 में "प्रेरणा" अनुभाग देखें ।
जनरेटर का एक गैर-स्पष्ट उपयोग रुकावटपूर्ण कार्य बना रहा है, जो आपको थ्रेड्स का उपयोग नहीं करते समय अपडेट यूआई या कई काम "एक साथ" (इंटरएलेव्ड, वास्तव में) चलाने की सुविधा देता है।
मुझे यह स्पष्टीकरण मिल गया है जो मेरे संदेह को दूर करता है। क्योंकि इस बात की संभावना है कि जो व्यक्ति नहीं जानता है वह Generators
भी इसके बारे में नहीं जानता हैyield
वापसी
रिटर्न स्टेटमेंट वह जगह है जहां सभी स्थानीय चर नष्ट हो जाते हैं और परिणामी मूल्य कॉलर को वापस (लौटा) दिया जाता है। उसी फ़ंक्शन को कुछ समय बाद कहा जाना चाहिए, फ़ंक्शन को चर का एक नया नया सेट मिलेगा।
मान जाना
लेकिन क्या होगा अगर एक समारोह से बाहर निकलने पर स्थानीय चर नहीं फेंके जाते? इसका तात्पर्य यह है कि हम resume the function
जहां से छूटे थे, वहां जा सकते हैं । यह वह जगह है जहां अवधारणा generators
पेश की जाती है और yield
कथन फिर से शुरू होता है जहां function
बाईं ओर।
def generate_integers(N):
for i in xrange(N):
yield i
In [1]: gen = generate_integers(3)
In [2]: gen
<generator object at 0x8117f90>
In [3]: gen.next()
0
In [4]: gen.next()
1
In [5]: gen.next()
तो यही अजगर के बीच return
और yield
बयानों में अंतर है ।
यील्ड स्टेटमेंट वह है जो फंक्शन को जनरेटर फंक्शन बनाता है।
तो जनरेटर बनाने के लिए एक सरल और शक्तिशाली उपकरण हैं। उन्हें नियमित कार्यों की तरह लिखा जाता है, लेकिन yield
जब भी वे डेटा वापस करना चाहते हैं तो वे कथन का उपयोग करते हैं। हर बार अगला () कहा जाता है, जनरेटर फिर से शुरू होता है जहां इसे छोड़ दिया गया था (यह सभी डेटा मानों को याद करता है और कौन सा कथन अंतिम बार निष्पादित किया गया था)।
मान लें कि आपके MySQL तालिका में 100 मिलियन डोमेन हैं, और आप प्रत्येक डोमेन के लिए एलेक्सा रैंक अपडेट करना चाहेंगे।
पहली बात आपको डेटाबेस से अपने डोमेन नामों का चयन करना है।
मान लें कि आपका टेबल नाम है domains
और कॉलम नाम है domain
।
यदि आप इसका उपयोग करते हैं तो SELECT domain FROM domains
यह 100 मिलियन पंक्तियों को वापस करने जा रहा है जो बहुत अधिक मेमोरी का उपभोग करने वाला है। तो आपका सर्वर क्रैश हो सकता है।
इसलिए आपने प्रोग्राम को बैचों में चलाने का निर्णय लिया। मान लीजिए कि हमारे बैच का आकार 1000 है।
हमारे पहले बैच में हम पहली 1000 पंक्तियों को क्वेरी करेंगे, प्रत्येक डोमेन के लिए एलेक्सा रैंक की जांच करेंगे और डेटाबेस पंक्ति को अपडेट करेंगे।
हमारे दूसरे बैच में हम अगली 1000 पंक्तियों पर काम करेंगे। हमारे तीसरे बैच में यह 2001 से 3000 तक और इसी तरह रहेगा।
अब हमें एक जनरेटर फ़ंक्शन की आवश्यकता है जो हमारे बैचों को उत्पन्न करता है।
यहाँ हमारे जनरेटर समारोह है:
def ResultGenerator(cursor, batchsize=1000):
while True:
results = cursor.fetchmany(batchsize)
if not results:
break
for result in results:
yield result
जैसा कि आप देख सकते हैं, हमारा कार्य yield
परिणाम को प्रभावित करता है। यदि आपने return
इसके बजाय कीवर्ड का उपयोग किया है yield
, तो यह वापस आने के बाद पूरा कार्य समाप्त हो जाएगा।
return - returns only once
yield - returns multiple times
यदि कोई फंक्शन कीवर्ड का उपयोग करता है yield
तो यह एक जनरेटर है।
अब आप इस तरह से पुनरावृत्ति कर सकते हैं:
db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
doSomethingWith(result)
db.close()
बफरिंग। जब यह बड़ी मात्रा में डेटा लाने के लिए कुशल है, लेकिन इसे छोटे टुकड़ों में संसाधित करें, तो एक जनरेटर मदद कर सकता है:
def bufferedFetch():
while True:
buffer = getBigChunkOfData()
# insert some code to break on 'end of data'
for i in buffer:
yield i
उपरोक्त आपको आसानी से प्रसंस्करण से बफरिंग को अलग करने देता है। उपभोक्ता फ़ंक्शन अब केवल बफरिंग के बारे में चिंता किए बिना एक-एक करके मान प्राप्त कर सकता है।
मैंने पाया है कि जनरेटर आपके कोड को साफ करने और कोड को एन्कैप्सुलेट और संशोधित करने के लिए एक बहुत ही अनोखा तरीका देने में बहुत सहायक हैं। ऐसी स्थिति में जब आपको अपने आंतरिक प्रसंस्करण के आधार पर मूल्यों को लगातार थूकने के लिए कुछ की आवश्यकता होती है और जब आपके कोड में कहीं से कुछ बुलाया जाना चाहिए (और उदाहरण के लिए केवल एक लूप या ब्लॉक के भीतर नहीं), जनरेटर की सुविधा है उपयोग।
एक सार उदाहरण एक फाइबोनैचि संख्या जनरेटर होगा जो लूप के भीतर नहीं रहता है और जब इसे कहीं से भी कॉल किया जाता है तो हमेशा क्रम में अगला नंबर लौटाएगा:
def fib():
first = 0
second = 1
yield first
yield second
while 1:
next = first + second
yield next
first = second
second = next
fibgen1 = fib()
fibgen2 = fib()
अब आपके पास दो फाइबोनैचि संख्या जनरेटर ऑब्जेक्ट हैं जिन्हें आप अपने कोड में कहीं से भी कॉल कर सकते हैं और वे क्रम में हमेशा बड़े फाइबोनैचि संख्याओं को क्रम में वापस करेंगे:
>>> fibgen1.next(); fibgen1.next(); fibgen1.next(); fibgen1.next()
0
1
1
2
>>> fibgen2.next(); fibgen2.next()
0
1
>>> fibgen1.next(); fibgen1.next()
3
5
जनरेटर के बारे में प्यारी बात यह है कि वे वस्तुओं को बनाने के हुप्स के माध्यम से जाने के बिना राज्य का अतिक्रमण करते हैं। उनके बारे में सोचने का एक तरीका "कार्यों" के रूप में है जो उनकी आंतरिक स्थिति को याद करते हैं।
मुझे पायथन जेनरेटरों से फिबोनाची उदाहरण मिला - वे क्या हैं? और थोड़ी कल्पना के साथ, आप कई अन्य स्थितियों के साथ आ सकते हैं जहां जनरेटर for
लूप और अन्य पारंपरिक पुनरावृत्तियों के निर्माण के लिए एक बढ़िया विकल्प बनाते हैं ।
सरल स्पष्टीकरण: एक for
बयान पर विचार करें
for item in iterable:
do_stuff()
बहुत समय, सभी वस्तुओं iterable
को शुरू से वहाँ रहने की आवश्यकता नहीं है, लेकिन मक्खी पर उत्पन्न किया जा सकता है जैसा कि वे आवश्यक हैं। यह दोनों में बहुत अधिक कुशल हो सकता है
अन्य बार, आप समय से पहले सभी वस्तुओं को नहीं जानते हैं। उदाहरण के लिए:
for command in user_input():
do_stuff_with(command)
आपके पास पहले से सभी उपयोगकर्ता के आदेशों को जानने का कोई तरीका नहीं है, लेकिन आप एक अच्छा लूप का उपयोग कर सकते हैं यदि आपके पास एक जनरेटर है जो आपको आदेश दे रहा है:
def user_input():
while True:
wait_for_command()
cmd = get_command()
yield cmd
जनरेटर के साथ आप अनंत अनुक्रमों पर भी पुनरावृत्ति कर सकते हैं, जो निश्चित रूप से कंटेनरों पर पुनरावृत्ति करते समय संभव नहीं है।
itertool
लिए एक उपाय है - देखें cycles
।
मेरे पसंदीदा उपयोग "फिल्टर" और "कम" ऑपरेशन हैं।
मान लीजिए कि हम एक फ़ाइल पढ़ रहे हैं, और केवल वही लाइनें चाहते हैं जो "##" से शुरू होती हैं।
def filter2sharps( aSequence ):
for l in aSequence:
if l.startswith("##"):
yield l
हम एक उचित लूप में जनरेटर फ़ंक्शन का उपयोग कर सकते हैं
source= file( ... )
for line in filter2sharps( source.readlines() ):
print line
source.close()
कम उदाहरण समान है। मान लीजिए कि हमारे पास एक फ़ाइल है जहाँ हमें <Location>...</Location>
लाइनों के ब्लॉक का पता लगाने की आवश्यकता है । [एचटीएमएल टैग नहीं, लेकिन टैग जैसी दिखने वाली लाइनें।]
def reduceLocation( aSequence ):
keep= False
block= None
for line in aSequence:
if line.startswith("</Location"):
block.append( line )
yield block
block= None
keep= False
elif line.startsWith("<Location"):
block= [ line ]
keep= True
elif keep:
block.append( line )
else:
pass
if block is not None:
yield block # A partial block, icky
फिर, हम इस जनरेटर का उपयोग लूप के लिए उचित तरीके से कर सकते हैं।
source = file( ... )
for b in reduceLocation( source.readlines() ):
print b
source.close()
विचार यह है कि एक जनरेटर फ़ंक्शन हमें एक अनुक्रम को फ़िल्टर करने या कम करने की अनुमति देता है, जो एक समय में एक और अनुक्रम एक मूल्य का उत्पादन करता है।
fileobj.readlines()
जनरेटर का उपयोग करने के उद्देश्य को पराजित करते हुए, पूरी फ़ाइल को स्मृति में एक सूची में पढ़ा जाएगा। चूंकि फ़ाइल ऑब्जेक्ट पहले से ही चलने योग्य हैं, आप for b in your_generator(fileobject):
इसके बजाय उपयोग कर सकते हैं । इस तरह आपकी फ़ाइल को एक बार में एक लाइन पढ़ी जाएगी, ताकि पूरी फ़ाइल पढ़ने से बच सके।
एक व्यावहारिक उदाहरण जहां आप एक जनरेटर का उपयोग कर सकते हैं यदि आपके पास किसी प्रकार का आकार है और आप इसके कोनों, किनारों या जो भी हो, उस पर पुनरावृति करना चाहते हैं। अपने स्वयं के प्रोजेक्ट के लिए ( यहाँ स्रोत कोड ) मेरे पास एक आयत थी:
class Rect():
def __init__(self, x, y, width, height):
self.l_top = (x, y)
self.r_top = (x+width, y)
self.r_bot = (x+width, y+height)
self.l_bot = (x, y+height)
def __iter__(self):
yield self.l_top
yield self.r_top
yield self.r_bot
yield self.l_bot
अब मैं इसके कोनों पर एक आयत और लूप बना सकता हूँ:
myrect=Rect(50, 50, 100, 100)
for corner in myrect:
print(corner)
इसके बजाय __iter__
आपके पास एक विधि iter_corners
और कॉल हो सकती है for corner in myrect.iter_corners()
। यह उपयोग करने के लिए सिर्फ और अधिक सुरुचिपूर्ण है __iter__
तब से हम सीधे for
अभिव्यक्ति में वर्ग उदाहरण नाम का उपयोग कर सकते हैं ।
यहाँ पर कुछ अच्छे उत्तर हैं, हालाँकि, मैं पायथन फंक्शनल प्रोग्रामिंग ट्यूटोरियल के बारे में पूरी तरह से पढ़ने की सलाह देता हूँ, जो जनरेटर के कुछ अधिक शक्तिशाली उपयोग-मामलों की व्याख्या करने में मदद करता है।
चूंकि जनरेटर की भेजने की विधि का उल्लेख नहीं किया गया है, यहां एक उदाहरण है:
def test():
for i in xrange(5):
val = yield
print(val)
t = test()
# Proceed to 'yield' statement
next(t)
# Send value to yield
t.send(1)
t.send('2')
t.send([3])
यह एक चालू जनरेटर को एक मूल्य भेजने की संभावना दर्शाता है। नीचे दिए गए वीडियो में जनरेटर पर एक और अधिक उन्नत पाठ्यक्रम ( yield
अन्वेषण से, समानांतर प्रसंस्करण के लिए जनरेटर, पुनरावृत्ति से बचने, आदि)
सामान के ढेर। किसी भी समय आप वस्तुओं का एक क्रम उत्पन्न करना चाहते हैं, लेकिन उन सभी को एक बार में सूची में 'भौतिक' नहीं करना चाहते हैं। उदाहरण के लिए, आपके पास एक साधारण जनरेटर हो सकता है जो प्रमुख संख्या देता है:
def primes():
primes_found = set()
primes_found.add(2)
yield 2
for i in itertools.count(1):
candidate = i * 2 + 1
if not all(candidate % prime for prime in primes_found):
primes_found.add(candidate)
yield candidate
आप बाद के प्राइम के उत्पादों को उत्पन्न करने के लिए इसका उपयोग कर सकते हैं:
def prime_products():
primeiter = primes()
prev = primeiter.next()
for prime in primeiter:
yield prime * prev
prev = prime
ये काफी तुच्छ उदाहरण हैं, लेकिन आप देख सकते हैं कि यह बड़े (संभावित अनंत!) डेटासेट के प्रसंस्करण के लिए कैसे उपयोगी हो सकता है, उन्हें अग्रिम में उत्पन्न किए बिना, जो कि अधिक स्पष्ट उपयोगों में से एक है।
N तक के प्राइम नंबरों को प्रिंट करने के लिए भी अच्छा है:
def genprime(n=10):
for num in range(3, n+1):
for factor in range(2, num):
if num%factor == 0:
break
else:
yield(num)
for prime_num in genprime(100):
print(prime_num)