कोर्स
Python फ़ंक्शंस में हमेशा return स्टेटमेंट नहीं होता। जनरेटर फ़ंक्शन वे फ़ंक्शन होते हैं जिनमें return के बजाय yield कीवर्ड होता है।
ये फ़ंक्शंस जनरेटर इटरेटर्स बनाते हैं, जो डेटा की एक स्ट्रीम का प्रतिनिधित्व करने वाली ऑब्जेक्ट्स होते हैं। किसी इटरेटर द्वारा दर्शाए गए एलिमेंट्स तभी बनाए और प्रदान किए जाते हैं जब उनकी आवश्यकता होती है। इस प्रकार के मूल्यांकन को अक्सर लेज़ी इवैल्युएशन कहा जाता है।
बड़े डेटा सेट्स के साथ काम करते समय, जनरेटर्स सूचियों, ट्यूपल्स और अन्य डेटा स्ट्रक्चर्स में डेटा संग्रहीत करने के मुकाबले मेमोरी-कुशल विकल्प देते हैं, क्योंकि उन संरचनाओं में हर तत्व के लिए मेमोरी स्पेस चाहिए होता है। जनरेटर फ़ंक्शन अनंत इटरेटर्स भी बना सकते हैं, जो सूची और ट्यूपल जैसी ईगरली इवैल्युएटेड संरचनाओं के साथ संभव नहीं है।
शुरू करने से पहले, आइए फ़ंक्शंस और जनरेटर्स के बीच के अंतर दोहरा लें:
|
फ़ीचर |
फ़ंक्शन |
जनरेटर |
|
मान उत्पादन |
सभी मान एक साथ लौटाता है |
आवश्यकता पड़ने पर एक-एक करके मान देता है |
|
एक्सीक्यूशन |
लौटाने से पहले पूरा चलकर समाप्त होता है |
एक मान देने के बाद रुकता है, अगला मान माँगे जाने पर फिर से शुरू होता है |
|
कीवर्ड |
return |
yield |
|
मेमोरी उपयोग |
संभावित रूप से अधिक, पूरी सिक्वेंस मेमोरी में संग्रहीत |
कम, केवल वर्तमान मान और अगली स्थिति संग्रहीत |
|
इटरेशन |
कई बार इटरेट किया जा सकता है, पर पूरी सिक्वेंस स्टोर करनी पड़ती है |
सिंगल-पास इटरेशन के लिए बनाया गया, बड़े या अनंत सिक्वेंस के लिए अधिक कुशल |
Python के yield का उपयोग कर जनरेटर फ़ंक्शन बनाना
Python में जनरेटर शब्द जनरेटर इटरेटर या जनरेटर फ़ंक्शन के लिए प्रयोग हो सकता है। ये Python में अलग-अलग पर संबंधित ऑब्जेक्ट्स हैं। इस ट्यूटोरियल में भ्रम से बचने के लिए अक्सर पूर्ण शब्दों का उपयोग किया गया है।
पहले जनरेटर फ़ंक्शंस को समझते हैं। जनरेटर फ़ंक्शन सामान्य फ़ंक्शन जैसा दिखता है, लेकिन इसमें return की जगह yield कीवर्ड होता है।
जब कोई Python प्रोग्राम किसी जनरेटर फ़ंक्शन को कॉल करता है, तो यह एक जनरेटर इटरेटर बनाता है। इटरेटर्स मांग पर मान देते हैं और तब तक अपने निष्पादन को रोकते हैं जब तक अगला मान आवश्यक न हो। इस अवधारणा को समझाने और सामान्य तथा जनरेटर फ़ंक्शंस के बीच का अंतर दिखाने के लिए एक उदाहरण देखते हैं।
सामान्य फ़ंक्शन का उपयोग
सबसे पहले, एक सामान्य फ़ंक्शन परिभाषित करते हैं, जिसमें return स्टेटमेंट है। यह फ़ंक्शन शब्दों की एक सिक्वेंस और एक अक्षर लेता है, और प्रत्येक शब्द में उस अक्षर के बार-बार आने की संख्या वाली एक सूची लौटाता है:
def find_letter_occurrences(words, letter):
output = []
for word in words:
output.append(word.count(letter))
return output
print(
find_letter_occurrences(["apple", "banana", "cherry"], "a")
)
[1, 3, 0]
यह फ़ंक्शन 1, 3 और 0 वाली सूची आउटपुट करता है, क्योंकि apple में a एक बार, banana में a तीन बार और cherry में एक बार भी नहीं आता। इसी फ़ंक्शन को खाली सूची इनिशियलाइज़ कर .append() के बजाय लिस्ट कॉम्प्रिहेंशन का उपयोग कर फिर से लिखा जा सकता है:
def find_letter_occurrences(words, letter):
return [word.count(letter) for word in words]
यह सामान्य फ़ंक्शन जब भी कॉल होता है, सभी परिणामों वाली एक सूची लौटाता है। हालांकि, यदि शब्दों की सूची बड़ी है, तो इस फ़ंक्शन को कॉल करने पर मेमोरी की माँग बढ़ जाती है क्योंकि प्रोग्राम मूल सूची के समान आकार की एक नई सूची बनाता और संग्रहीत करता है। यदि यह फ़ंक्शन कई इनपुट आर्ग्युमेंट्स पर बार-बार उपयोग होता है, या इसी तरह के फ़ंक्शंस मूल डेटा पर अन्य ऑपरेशंस करते हैं, तो मेमोरी पर दबाव तेजी से बढ़ सकता है।
जनरेटर फ़ंक्शन का उपयोग
इसके बजाय एक जनरेटर फ़ंक्शन इस्तेमाल किया जा सकता है:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
print(output)
<generator object find_letter_occurrences at 0x102935e00>
इस फ़ंक्शन में return के बजाय yield कीवर्ड शामिल है। यह जनरेटर फ़ंक्शन कॉल होने पर एक जनरेटर ऑब्जेक्ट लौटाता है, जिसे output को सौंपा जाता है। यह ऑब्जेक्ट एक इटरेटर है। इसमें प्रत्येक शब्द में अक्षर के बार-बार आने की संख्या का डेटा मौजूद नहीं होता। इसके बजाय जनरेटर जब आवश्यकता होगी तब मान बनाएगा और देगा। आइए इस जनरेटर इटरेटर से पहला मान प्राप्त करें:
print(next(output))
1
इनबिल्ट फ़ंक्शन next() इटरेटर से अगला मान प्राप्त करने का एक तरीका है। इस ट्यूटोरियल में आगे अन्य तरीकों को भी देखेंगे।
जनरेटर फ़ंक्शन का कोड तब तक निष्पादित होता है जब तक प्रोग्राम yield वाले लाइन तक नहीं पहुँचता। इस उदाहरण में, for लूप अपनी पहली इटरेशन शुरू करता है और सूची words से पहला तत्व लाता है। .count() स्ट्रिंग मेथड एक पूर्णांक लौटाता है, जो इस मामले में 1 है, क्योंकि apple में a एक बार आता है। जनरेटर यह मान देता है, जो next(output) द्वारा लौटाया जाता है।
जनरेटर output इस बिंदु पर अपने निष्पादन को रोक देता है। यानी, जनरेटर ने for लूप की पहली इटरेशन पूरी कर ली है और शब्दों की सूची के पहले शब्द में a के आवृत्तियों की संख्या खोज ली है। अब यह फिर से आवश्यक होने तक प्रतीक्षा कर रहा है।
यदि इनबिल्ट next() को दोबारा output के साथ कॉल किया जाए, तो जनरेटर वहीं से निष्पादन फिर से शुरू करेगा जहाँ वह रुका था:
print(next(output))
3
जनरेटर for लूप की पहली इटरेशन में yield वाली लाइन से आगे बढ़ता है। चूँकि for लूप में आगे कोई और कोड नहीं है, यह लूप के शीर्ष पर लौटता है और सूची words से दूसरा तत्व लाता है। इस बार .count() 3 लौटाता है, और यही मान दिया जाता है। जनरेटर फिर से इसी बिंदु पर रुक जाता है।
तीसरी बार next() कॉल करने से निष्पादन फिर शुरू होता है:
print(next(output))
0
दूसरी इटरेशन for लूप के अंत तक पहुँचती है और तीसरी इटरेशन पर जाती है। कोड फिर से yield वाली लाइन तक आता है, और इस बार 0 देता है क्योंकि cherry में a का कोई भी आवृत्ति नहीं है।
जनरेटर फिर से रुकता है। प्रोग्राम इस जनरेटर का भविष्य तभी तय करता है जब हम चौथी बार next() कॉल करते हैं:
print(next(output))
Traceback (most recent call last):
...
StopIteration
for लूप की तीसरी इटरेशन के अंत से निष्पादन फिर शुरू होता है। हालाँकि, अब सूची words में और तत्व नहीं बचे हैं, इसलिए लूप समाप्त हो चुका है। जनरेटर StopIteration अपवाद उठाता है।
अधिकांश उपयोग मामलों में, जनरेटर के तत्वों तक सीधे next() के माध्यम से नहीं, बल्कि किसी अन्य इटरेटिव प्रक्रिया के माध्यम से पहुँचा जाता है। StopIteration अपवाद इटरेशन प्रक्रिया के अंत का संकेत देता है। इसे हम इस ट्यूटोरियल के अगले भाग में और देखेंगे।
Python में तब एक और तरीका है जनरेटर इटरेटर बनाने का जब उनका ऑपरेशन एक ही अभिव्यक्ति से व्यक्त किया जा सकता है, जैसा कि पिछले उदाहरण में है। जनरेटर इटरेटर output को जनरेटर अभिव्यक्ति का उपयोग करके बनाया जा सकता है:
words = ["apple", "banana", "cherry"]
letter = "a"
output = (word.count(letter) for word in words)
print(next(output))
print(next(output))
print(next(output))
print(next(output))
1
3
0
Traceback (most recent call last):
...
StopIteration
output को सौंपे गए कोष्ठकों में दी गई अभिव्यक्ति एक जनरेटर अभिव्यक्ति है, जो जनरेटर फ़ंक्शन find_letter_occurrences() द्वारा उत्पन्न जनरेटर इटरेटर जैसा ही इटरेटर बनाती है।
आइए इस भाग को एक और जनरेटर फ़ंक्शन के उदाहरण के साथ समाप्त करें ताकि यह रेखांकित हो सके कि हर बार जब किसी तत्व की आवश्यकता होती है, तो निष्पादन कैसे रुकता और फिर शुरू होता है:
def show_status():
print("Start")
yield
print("Middle")
yield
print("End")
yield
status = show_status()
next(status)
Start
इस जनरेटर फ़ंक्शन में कोई लूप नहीं है। इसके बजाय, इसमें तीन पंक्तियाँ हैं जिनमें yield कीवर्ड है। जब show_status() जनरेटर फ़ंक्शन को कॉल किया जाता है, तो कोड एक जनरेटर इटरेटर status बनाता है। पहली बार next(status) कॉल करने पर जनरेटर निष्पादन शुरू करता है। यह स्ट्रिंग "Start" प्रिंट करता है और पहले yield अभिव्यक्ति के बाद रुक जाता है। चूँकि yield कीवर्ड के बाद कोई ऑब्जेक्ट नहीं है, जनरेटर None देता है।
प्रोग्राम स्ट्रिंग "Middle" तभी प्रिंट करता है जब next() दूसरी बार कॉल किया जाए:
next(status)
Middle
जनरेटर दूसरी yield अभिव्यक्ति के बाद रुकता है। तीसरी बार next() कॉल करने पर अंतिम स्ट्रिंग "End" प्रिंट होती है:
next(status)
End
जनरेटर अंतिम yield अभिव्यक्ति पर रुकता है। अगली बार जब प्रोग्राम इस जनरेटर इटरेटर से कोई मान माँगेगा, तो यह StopIteration अपवाद उठाएगा:
next(status)
Traceback (most recent call last):
...
StopIteration
आगे वाले भाग में हम जनरेटर्स के और उपयोगों को देखेंगे।
जनरेटर इटरेटर्स के साथ काम करना
जनरेटर फ़ंक्शंस जनरेटर इटरेटर्स बनाते हैं, और इटरेटर्स इटेरेबल होते हैं। इसे खोलकर समझें। हर बार जब प्रोग्राम किसी जनरेटर फ़ंक्शन को कॉल करता है, तो यह एक इटरेटर बनाता है। चूँकि इटरेटर्स इटेरेबल होते हैं, उन्हें for लूप्स और अन्य इटरेटिव प्रक्रियाओं में इस्तेमाल किया जा सकता है।
इसलिए, next() इनबिल्ट फ़ंक्शन इटरेटर के एलिमेंट्स तक पहुँचने का एकमात्र तरीका नहीं है। इस भाग में जनरेटर्स के साथ काम करने के अन्य तरीकों का अन्वेषण करेंगे।
Python के इटरेशन प्रोटोकॉल का जनरेटर इटरेटर्स के साथ उपयोग
आइए इस ट्यूटोरियल के पहले के भाग में दिए गए एक जनरेटर फ़ंक्शन पर लौटते हैं:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
for value in output:
print(value)
1
3
0
कई बार next() का उपयोग करने के बजाय, इस संस्करण में जनरेटर इटरेटर output को for लूप में इस्तेमाल किया गया है। चूँकि इटरेटर्स इटेरेबल होते हैं, वे for लूप्स में उपयोग किए जा सकते हैं। लूप जनरेटर इटरेटर से तब तक आइटम लाता है जब तक कोई मान शेष न रहे।
सूची और ट्यूपल जैसे डेटा स्ट्रक्चर्स के विपरीत, किसी इटरेटर का उपयोग केवल एक बार किया जा सकता है। यदि हम वही for लूप दूसरी बार चलाने की कोशिश करते हैं, तो कोड फिर से मान प्रिंट नहीं करेगा:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
print("First attempt:")
for value in output:
print(value)
print("Second attempt:")
for value in output:
print(value)
First attempt:
1
3
0
Second attempt:
पहले for लूप द्वारा इटरेटर समाप्त हो चुका है, इसलिए अब यह और मान नहीं दे सकता। यदि जनरेटर की फिर से आवश्यकता हो जब वह समाप्त हो चुका हो, तो हमें जनरेटर फ़ंक्शन से एक नया जनरेटर इटरेटर बनाना होगा।
किसी प्रोग्राम में एक ही समय पर कई जनरेटर इटरेटर्स होना भी संभव है:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
first_output = find_letter_occurrences(words, letter)
second_output = find_letter_occurrences(words, letter)
print("First value of first_output:")
print(next(first_output))
print("Values of second_output:")
for value in second_output:
print(value)
print("Remaining values of first_output:")
for value in first_output:
print(value)
First value of first_output:
1
Values of second_output:
1
3
0
Remaining values of first_output:
3
0
जनरेटर फ़ंक्शन find_letter_occurrences() दो जनरेटर इटरेटर्स बनाता है: first_output और second_output। यद्यपि दोनों इटरेटर्स सूची words में एक ही डेटा का संदर्भ देते हैं, वे एक-दूसरे से स्वतंत्र रूप से आगे बढ़ते हैं।
इस उदाहरण में first_output से पहला मान next() का उपयोग कर प्राप्त किया जाता है। जनरेटर इटरेटर 1 देता है और इस बिंदु पर रुकता है। इसके बाद प्रोग्राम second_output के माध्यम से लूप करता है। चूँकि इस जनरेटर ने अभी तक कोई मान नहीं दिया है, लूप दूसरे इटरेटर द्वारा दिए गए सभी मानों से होकर गुजरता है। अंत में, एक और for लूप first_output के माध्यम से इटरेट करता है। हालाँकि, इस इटरेटर ने पहले ही अपना पहला मान दे दिया है। लूप इस इटरेटर के शेष मानों से होकर गुजरता है।
for लूप जनरेटर इटरेटर्स पर इटरेट करने की एकमात्र प्रक्रिया नहीं है:
print(*find_letter_occurrences(words, letter))
print(sorted(find_letter_occurrences(words, letter)))
1 3 0
[0, 1, 3]
इन उदाहरणों में, इटरेटर को किसी वेरिएबल को सौंपने के बजाय, प्रोग्राम सीधे जनरेटर फ़ंक्शन को कॉल करता है ताकि जनरेटर इटरेटर बनाया और उपयोग किया जा सके। पहले उदाहरण में, इटरेटर को स्टार नोटेशन का उपयोग करके अनपैक किया जाता है। यह प्रक्रिया for लूप के समान इटरेशन प्रोटोकॉल पर निर्भर करती है।
दूसरे उदाहरण में, जनरेटर इटरेटर को इनबिल्ट sorted() को पास किया जाता है, जिसे एक इटेरेबल आर्ग्युमेंट चाहिए। जनरेटर्स इटेरेबल होते हैं, इसलिए जहाँ कहीं भी Python में इटरेशन होता है, वहाँ उनका उपयोग किया जा सकता है।
अनंत इटरेटर्स बनाना
एक जनरेटर एक मान देता है और अगला मान आवश्यक होने तक रुकता है। हर बार जब कोड इटरेटर से कोई मान माँगता है, जनरेटर फ़ंक्शन के भीतर का कोड तब तक निष्पादित होता है जब तक अगली yield अभिव्यक्ति का मूल्यांकन न हो जाए। अब तक के सभी उदाहरणों में, जनरेटर फ़ंक्शन में yield अभिव्यक्तियों की संख्या सीमित थी। हालाँकि, जनरेटर फ़ंक्शन में while लूप का उपयोग करके ऐसा जनरेटर बनाना संभव है जो अनंत संख्या में मान दे। निम्न उदाहरण में, जनरेटर रंगों की दी गई सूची में से यादृच्छिक रंग देता है:
import random
def get_color(colors):
while True:
yield random.choice(colors)
output_colors = get_color(["red", "green", "blue"])
print("First two colors:")
print(next(output_colors))
print(next(output_colors))
print("Next 10 colors using a 'for' loop:")
for _ in range(10):
print(next(output_colors))
First two colors:
green
red
Next 10 colors using a 'for' loop:
blue
green
green
green
red
red
red
blue
green
red
जनरेटर फ़ंक्शन get_color() में while लूप के भीतर yield अभिव्यक्ति है। इसलिए, अगला मान खोजते समय कोड को हमेशा एक और yield अभिव्यक्ति मिलेगी। जनरेटर इटरेटर output_colors इनपुट सूची से यादृच्छिक रूप से चुने गए रंगों की अनंत संख्या देता है। यह जनरेटर कभी समाप्त नहीं होगा।
सूची और ट्यूपल जैसी अनंत डेटा संरचनाएँ बनाना संभव नहीं है। जनरेटर्स किसी प्रोग्राम को अनंत इटेरेबल्स बनाने में सक्षम बनाते हैं। ध्यान दें कि यदि जनरेटर इटरेटर को सीधे for लूप में उपयोग किया जाए, तो लूप हमेशा चलता रहेगा।
उन्नत जनरेटर अवधारणाएँ
Python में जनरेटर्स के और भी उन्नत उपयोग हैं। इस भाग में हम इनमें से कुछ का अन्वेषण करेंगे।
जनरेटर में कोई ऑब्जेक्ट भेजना
जनरेटर अतिरिक्त डेटा भी स्वीकार कर सकते हैं, जिसका उपयोग कोड के मूल्यांकन के दौरान किया जा सकता है। yield कीवर्ड वाली स्टेटमेंट एक अभिव्यक्ति है जो किसी मान पर मूल्यांकित होती है। इस मान को जनरेटर फ़ंक्शन के भीतर एक वेरिएबल को सौंपा जा सकता है। इस अवधारणा को दिखाने के लिए एक बुनियादी उदाहरण से शुरू करते हैं:
def generator_function():
value = yield 1
print(f"The yield expression evaluates to: {value}")
value = yield 2
print(f"The yield expression evaluates to: {value}")
output = generator_function()
print(next(output))
print(next(output))
print(next(output))
1
The yield expression evaluates to: None
2
The yield expression evaluates to: None
Traceback (most recent call last):
...
StopIteration
Python का yield कीवर्ड एक ऐसी अभिव्यक्ति बनाता है जो किसी मान पर मूल्यांकित होती है। हालाँकि, जनरेटर फ़ंक्शन के भीतर इस अभिव्यक्ति का मान वह ऑब्जेक्ट नहीं होता जिसे जनरेटर देता है। पहले yield अभिव्यक्ति पर विचार करें। जनरेटर पूर्णांक 1 देता है। इसलिए, पहली बार print(next(output)) 1 प्रदर्शित करता है और जनरेटर का निष्पादन रुक जाता है।
लेकिन जनरेटर के भीतर yield अभिव्यक्ति एक ऑब्जेक्ट पर मूल्यांकित होती है, जिसे वेरिएबल नाम value को सौंपा जाता है। इस उदाहरण में, yield value को None सौंपता है। यही प्रक्रिया जनरेटर फ़ंक्शन में दूसरी बार yield होने पर भी दोहराई जाती है। तीसरी बार next() कॉल करने का उद्देश्य यह सुनिश्चित करना है कि जनरेटर फ़ंक्शन का सारा कोड चल जाए।
आइए दूसरी और तीसरी next() कॉल को जनरेटर क्लास की मेथड .send() से बदलते हैं:
def generator_function():
value = yield 1
print(f"The yield expression evaluates to: {value}")
value = yield 2
print(f"The yield expression evaluates to: {value}")
output = generator_function()
print(next(output))
print(output.send("Here's a value"))
print(output.send("Here's another value"))
1
The yield expression evaluates to: Here's a value
2
The yield expression evaluates to: Here's another value
Traceback (most recent call last):
...
StopIteration
जनरेटर फ़ंक्शन अपरिवर्तित है। जनरेटर को next() कॉल कर के शुरू किया जाता है, और कोड तब तक चलता है जब तक यह पहला पूर्णांक 1 नहीं देता। दूसरी बार next() का उपयोग करने के बजाय, प्रोग्राम output.send() को कॉल करता है। यह मेथड जनरेटर में कोई ऑब्जेक्ट भेजती है। इस उदाहरण में, ऑब्जेक्ट एक स्ट्रिंग है। जनरेटर फ़ंक्शन में yield अभिव्यक्ति इसी स्ट्रिंग पर मूल्यांकित होती है, जिसे value को सौंपा जाता है। इसलिए, जनरेटर अपने कोड में इस स्ट्रिंग का उपयोग कर सकता है।
दूसरी बार .send() को कॉल करने पर जनरेटर को नया ऑब्जेक्ट भेजा जाता है, जिसे उसी वेरिएबल value को सौंपा जाता है। अंतिम print() कॉल के बाद, चूँकि अब और yield अभिव्यक्तियाँ नहीं हैं, जनरेटर StopIteration उठाता है।
आइए .send() का एक और उदाहरण देखें। निम्न जनरेटर खाते का बैलेंस दिखाता है, और बैलेंस अपडेट भी किया जा सकता है:
def get_balance(start_balance):
balance = start_balance
while True:
amount = yield balance
if amount is not None:
balance += amount
current_balance = get_balance(100)
print(next(current_balance))
print(current_balance.send(10))
print(current_balance.send(-20))
print(next(current_balance))
100
110
90
90
जनरेटर फ़ंक्शन को कॉल करते समय एक शुरुआती बैलेंस चाहिए। balance का मान जनरेटर के कोड के चलते बदल सकता है। .send() के माध्यम से जनरेटर में भेजा गया कोई भी ऑब्जेक्ट amount को सौंपा जाता है। यदि जनरेटर बिना कोई ऑब्जेक्ट भेजे मान देता है, तो यह वेरिएबल None होगा; अन्यथा इसमें .send() द्वारा भेजा गया ऑब्जेक्ट होगा।
जनरेटर इटरेटर current_balance $100 के बैलेंस से शुरू होता है। जनरेटर को next() कॉल कर शुरू किया जाता है, जो कोड को तब तक चलाता है जब तक पहला मान न दे दे।
एक बार जनरेटर शुरू हो जाने पर, next() की जगह .send() का उपयोग कर निष्पादन फिर से शुरू किया जा सकता है। जनरेटर भेजे गए मान को बैलेंस में जोड़ता है। यदि कोई मान नहीं भेजा गया, जैसे कि दोबारा next() कॉल करने पर, तो जनरेटर अपरिवर्तित बैलेंस देता है।
किसी अन्य इटेरेबल से सीधे मान देना
Python जनरेटर्स yield from सिंटैक्स का उपयोग कर किसी अन्य जनरेटर या इटेरेबल से सीधे मान भी दे सकते हैं। आइए एक उदाहरण देखें, जिसमें जनरेटर फ़ंक्शन नेस्टेड सूची से मान देता है:
def flatten(nested_list):
for item in nested_list:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
nested_list = [1, [2, 3], [4, [5, 6]], 7]
print(list(flatten(nested_list)))
[1, 2, 3, 4, 5, 6, 7]
जनरेटर फ़ंक्शन एक सूची स्वीकार करता है, जिसमें नेस्टेड सूचियाँ भी हो सकती हैं। for लूप सूची के आइटम्स पर इटरेट करता है। बाहरी सूची का प्रत्येक आइटम या तो कोई मान होता है, यहाँ पूर्णांक, या फिर कोई और सूची। जब आइटम सूची नहीं होता, तो जनरेटर वही आइटम देता है।
लेकिन जब आइटम सूची होता है, तो जनरेटर भीतर की सूची को आर्ग्युमेंट के रूप में लेकर जनरेटर फ़ंक्शन flatten() को पुनः कॉल करता है। इससे एक और जनरेटर इटरेटर बनता है, जो अपनी डेटा सोर्स के रूप में भीतर की सूची का उपयोग करता है। यदि इस पंक्ति में yield अभिव्यक्ति का उपयोग किया जाता, तो पहला जनरेटर दूसरे जनरेटर को देता। इसके बजाय, yield from का उपयोग करने से पहला जनरेटर दूसरे जनरेटर से मान देता है।
सारांश: yield बनाम return
return और yield के साथ फ़ंक्शन डिफिनिशन देखने में समान लगते हैं, लेकिन उनका व्यवहार अलग होता है। मुख्य अंतर संक्षेप में:
|
सामान्य फ़ंक्शन |
जनरेटर फ़ंक्शन |
|
|
कीवर्ड |
return (यदि स्पष्ट रूप से न हो तो निहित) |
yield |
|
कॉल होने पर |
कोड return तक चलता है, फिर अंतिम मान लौटाता है |
एक जनरेटर इटरेटर बनाता है |
|
समापन |
return स्टेटमेंट द्वारा समाप्त |
yield पर रुका रहता है, बाद में फिर शुरू हो सकता है |
|
रिटर्न वैल्यू |
एकल ऑब्जेक्ट (कोई डेटा स्ट्रक्चर भी हो सकता है) |
जनरेटर इटरेटर |
|
Yield अभिव्यक्ति |
प्रासंगिक नहीं (स्टेटमेंट बनाती है) |
None या .send() से भेजा गया मान पर मूल्यांकित |
|
उपयोग के मामले |
अंतिम परिणाम लौटाने के लिए उपयुक्त |
डेटा स्ट्रीम बनाने के लिए उपयुक्त, विशेषकर बड़े या अनंत सिक्वेंस |
निष्कर्ष
Python का yield कीवर्ड किसी फ़ंक्शन में जनरेटर फ़ंक्शन परिभाषित करने के लिए उपयोग होता है। इन्हें कॉल करने पर ये जनरेटर इटरेटर्स बनाते हैं। जनरेटर्स Python में लेज़ी इवैल्युएशन का एक उदाहरण हैं, जिसमें अभिव्यक्तियाँ तब मूल्यांकित होती हैं जब मान की आवश्यकता होती है, न कि अभिव्यक्ति के निष्पादित होते समय। इसलिए, yield अभिव्यक्ति ऐसी डेटा स्ट्रीम बनाने में उपयोगी है जहाँ मान आवश्यकता अनुसार उत्पन्न होते हैं और उन्हें मेमोरी में संग्रहीत करने की ज़रूरत नहीं होती।
जब बड़े डेटा सेट्स पर कई ऑपरेशंस करने हों, तो दक्षता संबंधी विचार महत्वपूर्ण होते हैं। Python के जनरेटर इटरेटर्स बड़ी मात्रा में डेटा को कुशलतापूर्वक संसाधित करने के लिए आवश्यक प्रमुख उपकरणों में से एक हैं।
यदि आप Python के बारे में और जानना चाहते हैं, तो यह Python Developer करियर ट्रैक देखें।
मैंने यूनिवर्सिटी ऑफ माल्टा में स्नातक स्तर पर भौतिकी और गणित का अध्ययन किया। उसके बाद, मैं लंदन चला गया और इम्पीरियल कॉलेज से भौतिकी में पीएचडी प्राप्त की। मैंने मानव रेटिना की इमेजिंग के लिए नवीन ऑप्टिकल तकनीकों पर काम किया। अब, मैं Python के बारे में लिखने, Python पर संवाद करने और Python सिखाने पर ध्यान केंद्रित करता हूँ।
