क्या जनरेटर भाषा की सुविधा होना yield
एक अच्छा विचार है?
मैं पायथन परिप्रेक्ष्य से एक जोरदार हां के साथ इसका जवाब देना चाहता हूं , यह एक महान विचार है ।
मैं पहले आपके प्रश्न में कुछ प्रश्नों और मान्यताओं को संबोधित करके शुरू करूँगा, फिर बाद में पायथन में जनरेटर की व्यापकता और उनकी अनुचित उपयोगिता प्रदर्शित करता हूँ।
एक नियमित गैर-जनरेटर फ़ंक्शन के साथ आप इसे कॉल कर सकते हैं और यदि इसे समान इनपुट दिया जाता है, तो यह उसी आउटपुट को वापस कर देगा। उपज के साथ, यह अपनी आंतरिक स्थिति के आधार पर, अलग-अलग आउटपुट देता है।
यह गलत है। वस्तुओं पर तरीकों को अपने स्वयं के आंतरिक स्थिति के साथ कार्यों के रूप में सोचा जा सकता है। पायथन में, चूंकि सब कुछ एक वस्तु है, आप वास्तव में एक वस्तु से एक विधि प्राप्त कर सकते हैं, और उस विधि के चारों ओर से गुजर सकते हैं (जो उस वस्तु से बंधा है जो इसे आया था, इसलिए यह अपने राज्य को याद करता है)।
अन्य उदाहरणों में नेटवर्क, फ़ाइल सिस्टम और टर्मिनल जैसी इनपुट विधियों के साथ-साथ जानबूझकर यादृच्छिक कार्य शामिल हैं।
इस तरह का एक फ़ंक्शन भाषा के प्रतिमान में कैसे फिट होता है?
यदि भाषा प्रतिमान प्रथम श्रेणी के कार्यों जैसी चीजों का समर्थन करता है, और जनरेटर अन्य भाषा सुविधाओं जैसे Iterable प्रोटोकॉल का समर्थन करते हैं, तो वे मूल रूप से फिट होते हैं।
क्या यह वास्तव में किसी भी सम्मेलन को तोड़ता है?
नहीं, चूंकि यह भाषा में बेक किया गया है, इसलिए कन्वेंशन चारों ओर बनाए गए हैं और इसमें जनरेटर का उपयोग शामिल है (या आवश्यकता है!)।
क्या प्रोग्रामिंग भाषा संकलक / दुभाषियों को इस तरह की सुविधा को लागू करने के लिए किसी भी सम्मेलन से बाहर निकलना होगा
किसी भी अन्य विशेषता के साथ, संकलक को सुविधा का समर्थन करने के लिए डिज़ाइन करने की आवश्यकता है। पायथन के मामले में, फ़ंक्शन पहले से ही राज्य के साथ ऑब्जेक्ट हैं (जैसे डिफ़ॉल्ट तर्क और फ़ंक्शन एनोटेशन)।
क्या इस सुविधा के काम करने के लिए किसी भाषा को मल्टी-थ्रेडिंग को लागू करना पड़ता है, या इसे थ्रेडिंग तकनीक के बिना किया जा सकता है?
मजेदार तथ्य: डिफ़ॉल्ट पायथन कार्यान्वयन थ्रेडिंग का समर्थन नहीं करता है। इसमें ग्लोबल इंटरप्रेटर लॉक (GIL) की सुविधा है, इसलिए वास्तव में कुछ भी समवर्ती नहीं चल रहा है जब तक कि आप पायथन के एक अलग उदाहरण को चलाने के लिए दूसरी प्रक्रिया नहीं बना लेते हैं।
नोट: उदाहरण पायथन 3 में हैं
यील्ड से परे
जबकि yield
कीवर्ड का उपयोग किसी भी फ़ंक्शन में इसे जनरेटर में बदलने के लिए किया जा सकता है, यह एक बनाने का एकमात्र तरीका नहीं है। पायथन में जेनरेटर एक्सप्रेशंस की सुविधा है, जो एक अन्य पुनरावृत्ति (अन्य जनरेटर सहित) के संदर्भ में एक जनरेटर को स्पष्ट रूप से व्यक्त करने का एक शक्तिशाली तरीका है
>>> pairs = ((x,y) for x in range(10) for y in range(10) if y >= x)
>>> pairs
<generator object <genexpr> at 0x0311DC90>
>>> sum(x*y for x,y in pairs)
1155
जैसा कि आप देख सकते हैं, न केवल वाक्यविन्यास स्वच्छ और पठनीय है, बल्कि अंतर्निहित कार्य जैसे sum
जनरेटर स्वीकार करते हैं।
साथ में
बयान के साथ अजगर संवर्धन प्रस्ताव की जाँच करें । यह अन्य भाषाओं में कथन के साथ अपेक्षा से बहुत भिन्न है। मानक पुस्तकालय से थोड़ी मदद के साथ, पायथन के जनरेटर उनके लिए संदर्भ प्रबंधकों के रूप में खूबसूरती से काम करते हैं।
>>> from contextlib import contextmanager
>>> @contextmanager
def debugWith(arg):
print("preprocessing", arg)
yield arg
print("postprocessing", arg)
>>> with debugWith("foobar") as s:
print(s[::-1])
preprocessing foobar
raboof
postprocessing foobar
बेशक, मुद्रण चीजें सबसे अधिक उबाऊ चीज के बारे में हैं जो आप यहां कर सकते हैं, लेकिन यह दृश्यमान परिणाम दिखाता है। अधिक दिलचस्प विकल्पों में संसाधनों का ऑटो-प्रबंधन (फाइल / स्ट्रीम / नेटवर्क कनेक्शन खोलना और बंद करना) शामिल हैं, समवर्ती के लिए लॉक करना, किसी फ़ंक्शन को अस्थायी रूप से लपेटना या बदलना, और फिर डेटा पुनर्प्राप्त करना। यदि कॉलिंग फ़ंक्शन आपके कोड में कोड इंजेक्ट करने जैसा है, तो बयानों के साथ अन्य कोड में आपके कोड के कुछ हिस्सों को लपेटने जैसा है। हालाँकि आप इसका उपयोग करते हैं, यह भाषा संरचना में आसान हुक का एक ठोस उदाहरण है। यील्ड-आधारित जनरेटर संदर्भ प्रबंधक बनाने का एकमात्र तरीका नहीं है, लेकिन वे निश्चित रूप से एक सुविधाजनक हैं।
के लिए और आंशिक थकावट
पायथन में छोरों के लिए एक दिलचस्प तरीके से काम करते हैं। उनके पास निम्नलिखित प्रारूप हैं:
for <name> in <iterable>:
...
सबसे पहले, मैंने जिस अभिव्यक्ति को बुलाया <iterable>
है , उसका मूल्यांकन करने योग्य वस्तु प्राप्त करने के लिए मूल्यांकन किया जाता है। दूसरा, चलने वाले ने __iter__
उस पर कॉल किया है, और परिणामी पुनरावृत्ति को पर्दे के पीछे संग्रहीत किया जाता है। इसके बाद, __next__
इट्रेटर को उस नाम से पुकारने के लिए कहा जाता है , जिसमें आपने नाम डाला है <name>
। यह कदम दोहराता है जब तक कॉल __next__
फेंकता नहीं है StopIteration
। अपवाद लूप के लिए निगल लिया है, और निष्पादन वहाँ से जारी है।
जनरेटर पर वापस आना: जब आप __iter__
किसी जनरेटर पर कॉल करते हैं , तो यह बस खुद ही वापस आ जाता है।
>>> x = (a for a in "boring generator")
>>> id(x)
51502272
>>> id(x.__iter__())
51502272
इसका मतलब यह है कि आप उस चीज़ से कुछ अलग कर सकते हैं जिसे आप इसके साथ करना चाहते हैं, और उस व्यवहार को बीच में से बदल दें। नीचे, ध्यान दें कि एक ही जनरेटर का उपयोग दो छोरों में कैसे किया जाता है, और दूसरे में यह निष्पादित करना शुरू कर देता है जहां से यह पहले से रवाना हुआ था।
>>> generator = (x for x in 'more boring stuff')
>>> for letter in generator:
print(ord(letter))
if letter > 'p':
break
109
111
114
>>> for letter in generator:
print(letter)
e
b
o
r
i
n
g
s
t
u
f
f
आलसी मूल्यांकन
सूचियों की तुलना में जनरेटर के नीचे के पक्षों में से एक केवल एक चीज है जिसे आप जनरेटर में एक्सेस कर सकते हैं अगली चीज है जो इससे बाहर आती है। आप पिछले परिणाम के लिए वापस नहीं जा सकते हैं, या मध्यवर्ती परिणामों से गुजरने के बिना बाद में आगे कूद सकते हैं। इसका एक पक्ष यह है कि जनरेटर अपनी समकक्ष सूची की तुलना में लगभग कोई मेमोरी नहीं ले सकता है।
>>> import sys
>>> sys.getsizeof([x for x in range(10000)])
43816
>>> sys.getsizeof(range(10000000000))
24
>>> sys.getsizeof([x for x in range(10000000000)])
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
sys.getsizeof([x for x in range(10000000000)])
File "<pyshell#10>", line 1, in <listcomp>
sys.getsizeof([x for x in range(10000000000)])
MemoryError
जनरेटर भी आलसी जंजीर हो सकते हैं।
logfile = open("logs.txt")
lastcolumn = (line.split()[-1] for line in logfile)
numericcolumn = (float(x) for x in lastcolumn)
print(sum(numericcolumn))
पहली, दूसरी और तीसरी पंक्तियाँ सिर्फ एक जनरेटर को परिभाषित करती हैं, लेकिन कोई वास्तविक काम नहीं करती हैं। जब अंतिम पंक्ति को बुलाया जाता है, तो सम मूल्य के लिए संख्यात्मक अंक पूछते हैं, अंकीय रूप अंतिम रूप से मान की आवश्यकता होती है, लास्ट कालम लॉगफाइल से मान मांगता है, जो तब वास्तव में फ़ाइल से एक पंक्ति पढ़ता है। यह स्टैक तब तक खुला रहता है जब तक कि योग को अपना पहला पूर्णांक नहीं मिल जाता। फिर, दूसरी पंक्ति के लिए प्रक्रिया फिर से होती है। इस बिंदु पर, योग के दो पूर्णांक हैं, और यह उन्हें एक साथ जोड़ता है। ध्यान दें कि तीसरी पंक्ति अभी तक फ़ाइल से पढ़ी नहीं गई है। योग तब संख्यात्मक रूप से (शेष श्रृंखला से पूरी तरह से अनजान) मूल्यों को अनुरोध करने और उन्हें जोड़ने तक चला जाता है, जब तक कि संख्यात्मक कॉलम समाप्त नहीं हो जाता है।
यहाँ वास्तव में दिलचस्प हिस्सा यह है कि लाइनें पढ़ी जाती हैं, खपत की जाती हैं, और व्यक्तिगत रूप से खारिज कर दी जाती हैं। किसी भी बिंदु पर एक साथ मेमोरी में पूरी फाइल नहीं है। यदि यह लॉग फ़ाइल एक टेराबाइट है, तो क्या होगा? यह सिर्फ काम करता है, क्योंकि यह एक समय में केवल एक पंक्ति पढ़ता है।
निष्कर्ष
यह पायथन में जनरेटर के सभी उपयोगों की पूरी समीक्षा नहीं है। विशेष रूप से, मैं अनंत जनरेटर, राज्य मशीनों, वापस जाने वाले मूल्यों, और कोरटाइन के लिए उनके संबंधों को छोड़ देता हूं।
मेरा मानना है कि यह प्रदर्शित करने के लिए पर्याप्त है कि आपके पास एक स्वच्छ, उपयोगी भाषा सुविधा के रूप में जनरेटर हो सकते हैं।
yield
अनिवार्य रूप से एक राज्य इंजन है। यह हर बार एक ही परिणाम वापस करने के लिए नहीं है। निरपेक्षता के साथ यह क्या करेगा , अगली वस्तु को हर बार संलग्न किए जाने के बाद, एक परिमाण में वापस कर देगा। धागे की आवश्यकता नहीं है; वर्तमान स्थिति बनाए रखने के लिए आपको (अधिक या कम) बंद करने की आवश्यकता है।