Python Language
जेनरेटर
खोज…
परिचय
जनरेटर जनरेटर कार्यों ( yield
का उपयोग करके) या जनरेटर अभिव्यक्तियों (उपयोग (an_expression for x in an_iterator)
) द्वारा बनाए गए आलसी (an_expression for x in an_iterator)
।
वाक्य - विन्यास
- उपज
<expr>
-
<expr>
से उपज -
<var>
= उपज<expr>
- अगला (
<iter>
)
यात्रा
एक जनरेटर ऑब्जेक्ट पुनरावृत्त प्रोटोकॉल का समर्थन करता है । यही है, यह पायथन 3.x में एक next()
विधि ( __next__()
next()
प्रदान करता है, जिसका उपयोग इसके निष्पादन के माध्यम से करने के लिए किया जाता है, और इसकी __iter__
विधि स्वयं लौटती है। इसका मतलब यह है कि जनरेटर का उपयोग किसी भी भाषा के निर्माण में किया जा सकता है जो सामान्य चलने योग्य वस्तुओं का समर्थन करता है।
# naive partial implementation of the Python 2.x xrange()
def xrange(n):
i = 0
while i < n:
yield i
i += 1
# looping
for i in xrange(10):
print(i) # prints the values 0, 1, ..., 9
# unpacking
a, b, c = xrange(3) # 0, 1, 2
# building a list
l = list(xrange(10)) # [0, 1, ..., 9]
अगला () फ़ंक्शन
next()
बिल्ट-इन एक सुविधाजनक आवरण है, जिसका उपयोग किसी भी ईटरेटर (एक जनरेटर इटरेटर सहित) से एक मान प्राप्त करने के लिए किया जा सकता है और यदि एटरेटर समाप्त हो जाता है तो एक डिफ़ॉल्ट मान प्रदान करने के लिए।
def nums():
yield 1
yield 2
yield 3
generator = nums()
next(generator, None) # 1
next(generator, None) # 2
next(generator, None) # 3
next(generator, None) # None
next(generator, None) # None
# ...
वाक्यविन्यास next(iterator[, default])
। यदि इटरेटर समाप्त हो जाता है और एक डिफ़ॉल्ट मान पारित किया गया था, तो उसे वापस कर दिया जाता है। यदि कोई डिफ़ॉल्ट प्रदान नहीं किया गया था, तो StopIteration
उठाया जाता है।
एक जनरेटर के लिए वस्तुओं भेजना
एक जनरेटर से मान प्राप्त करने के अलावा, एक जनरेटर को send()
विधि का उपयोग करके भेजना संभव है।
def accumulator():
total = 0
value = None
while True:
# receive sent value
value = yield total
if value is None: break
# aggregate values
total += value
generator = accumulator()
# advance until the first "yield"
next(generator) # 0
# from this point on, the generator aggregates values
generator.send(1) # 1
generator.send(10) # 11
generator.send(100) # 111
# ...
# Calling next(generator) is equivalent to calling generator.send(None)
next(generator) # StopIteration
यहाँ क्या होता है निम्नलिखित है:
- जब आप पहली बार
next(generator)
कॉल करते हैं, तो प्रोग्राम पहलेyield
स्टेटमेंट को आगे बढ़ाता है, और उस बिंदु परtotal
का मान लौटाता है, जो कि इस बिंदु पर जनरेटर का निष्पादन 0. है। - जब आप
generator.send(x)
, तो दुभाषिया तर्कx
लेता है और इसे अंतिमyield
स्टेटमेंट का रिटर्न वैल्यू बनाता है, जिसेvalue
दिया जाता है। जनरेटर तब तक सामान्य रूप से आगे बढ़ता है, जब तक कि यह अगले मूल्य नहीं देता। - जब आप अंततः
next(generator)
कॉल करते हैं, तो प्रोग्राम इस तरह से व्यवहार करता है जैसे कि आप जनरेटर कोNone
भेज रहे हैं। कोई भी कुछ भी विशेषNone
, हालांकि, यह उदाहरण जनरेटर को रोकने के लिए पूछने के लिए विशेष मूल्य के रूप मेंNone
उपयोगNone
करता है।
जनक भाव
समझ-बूझ वाले वाक्यविन्यास का उपयोग करके जनरेटर पुनरावृत्तियों को बनाना संभव है।
generator = (i * 2 for i in range(3))
next(generator) # 0
next(generator) # 2
next(generator) # 4
next(generator) # raises StopIteration
यदि किसी फ़ंक्शन को आवश्यक रूप से एक सूची पास करने की आवश्यकता नहीं है, तो आप फ़ंक्शन कॉल के अंदर एक जनरेटर अभिव्यक्ति रखकर वर्णों को बचा सकते हैं (और पठनीयता में सुधार कर सकते हैं)। फ़ंक्शन कॉल से कोष्ठक स्पष्ट रूप से आपकी अभिव्यक्ति को एक जनरेटर अभिव्यक्ति बनाता है।
sum(i ** 2 for i in range(4)) # 0^2 + 1^2 + 2^2 + 3^2 = 0 + 1 + 4 + 9 = 14
इसके अतिरिक्त, आप मेमोरी पर बचत करेंगे क्योंकि पूरी सूची को लोड करने के बजाय आप (ऊपर के उदाहरण में [0, 1, 2, 3]
से अधिक पुनरावृत्ति कर रहे हैं), जनरेटर पाइथन को आवश्यकतानुसार मानों का उपयोग करने की अनुमति देता है।
परिचय
जेनरेटर अभिव्यक्तियाँ सूची, शब्दकोश और सेट समझ के समान हैं, लेकिन कोष्ठक के साथ संलग्न हैं। जब उन्हें किसी फ़ंक्शन कॉल के लिए एकमात्र तर्क के रूप में उपयोग किया जाता है, तो कोष्ठक मौजूद नहीं होते हैं।
expression = (x**2 for x in range(10))
यह उदाहरण 0 (जिसमें x = 0) सहित 10 प्रथम पूर्ण वर्ग उत्पन्न करता है।
जनरेटर के कार्य नियमित कार्यों के समान हैं, सिवाय इसके कि उनके शरीर में एक या अधिक yield
कथन हैं। ऐसे फ़ंक्शंस किसी भी मान को नहीं return
सकते हैं (यदि आप जनरेटर को जल्दी बंद करना चाहते हैं तो खाली return
एस की अनुमति है)।
def function():
for x in range(10):
yield x**2
यह जनरेटर फ़ंक्शन पिछले जनरेटर अभिव्यक्ति के बराबर है, यह एक ही आउटपुट करता है।
नोट : सभी जनरेटर अभिव्यक्तियों के अपने समान कार्य हैं, लेकिन इसके विपरीत नहीं।
यदि दोनों कोष्ठकों को अन्यथा दोहराया जाएगा तो जनरेटर अभिव्यक्ति का उपयोग बिना कोष्ठक के किया जा सकता है:
sum(i for i in range(10) if i % 2 == 0) #Output: 20
any(x = 0 for x in foo) #Output: True or False depending on foo
type(a > b for a in foo if a % 2 == 1) #Output: <class 'generator'>
के बजाय:
sum((i for i in range(10) if i % 2 == 0))
any((x = 0 for x in foo))
type((a > b for a in foo if a % 2 == 1))
लेकिन नहीं:
fooFunction(i for i in range(10) if i % 2 == 0,foo,bar)
return x = 0 for x in foo
barFunction(baz, a > b for a in foo if a % 2 == 1)
जनरेटर फ़ंक्शन को कॉल करने से एक जनरेटर ऑब्जेक्ट का उत्पादन होता है, जिसे बाद में खत्म किया जा सकता है। अन्य प्रकार के पुनरावृत्तियों के विपरीत, जनरेटर की वस्तुओं को केवल एक बार ट्रेस किया जा सकता है।
g1 = function()
print(g1) # Out: <generator object function at 0x1012e1888>
ध्यान दें कि जनरेटर के शरीर को तुरंत निष्पादित नहीं किया जाता है: जब आप ऊपर के उदाहरण में function()
को कॉल function()
, तो यह तुरंत जनरेटर ऑब्जेक्ट को लौटाता है, यहां तक कि पहले प्रिंट स्टेटमेंट को भी निष्पादित किए बिना। यह जेनरेटरों को सूची में वापस आने वाले कार्यों की तुलना में कम मेमोरी का उपभोग करने की अनुमति देता है, और यह उन जनरेटर को बनाने की अनुमति देता है जो असीम रूप से लंबे दृश्यों का उत्पादन करते हैं।
इस कारण से, जनरेटर का उपयोग अक्सर डेटा विज्ञान में किया जाता है, और अन्य संदर्भों में बड़ी मात्रा में डेटा शामिल होता है। एक और लाभ यह है कि अन्य कोड जनरेटर द्वारा उत्पादित मूल्यों का तुरंत उपयोग कर सकते हैं, बिना पूर्ण अनुक्रम के उत्पादन के लिए इंतजार किए बिना।
हालांकि, यदि आपको एक जनरेटर द्वारा उत्पादित मूल्यों का एक से अधिक बार उपयोग करने की आवश्यकता है, और यदि उन्हें भंडारण से अधिक लागत उत्पन्न होती है, तो अनुक्रम को फिर से तैयार करने की तुलना में उपज मानों को एक list
रूप में संग्रहीत करना बेहतर हो सकता है। अधिक विवरण के लिए नीचे 'जनरेटर को रीसेट करें' देखें।
आमतौर पर एक जनरेटर ऑब्जेक्ट का उपयोग लूप में, या किसी भी फ़ंक्शन में किया जाता है, जिसमें एक पुनरावृत्ति की आवश्यकता होती है:
for x in g1:
print("Received", x)
# Output:
# Received 0
# Received 1
# Received 4
# Received 9
# Received 16
# Received 25
# Received 36
# Received 49
# Received 64
# Received 81
arr1 = list(g1)
# arr1 = [], because the loop above already consumed all the values.
g2 = function()
arr2 = list(g2) # arr2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
चूंकि जनरेटर ऑब्जेक्ट पुनरावृत्त होते हैं, इसलिए कोई next()
फ़ंक्शन का उपयोग करके मैन्युअल रूप से उन पर पुनरावृति कर सकता है। ऐसा करने से प्रत्येक बाद के आह्वान पर एक-एक करके उपज मूल्य वापस आ जाएंगे।
हुड के तहत, हर बार जब आप एक जनरेटर पर next()
कॉल करते हैं, तो पायथन जनरेटर फ़ंक्शन के शरीर में बयानों को निष्पादित करता है जब तक कि यह अगले yield
बयान को हिट नहीं करता। इस बिंदु पर यह yield
कमांड का तर्क देता है, और उस बिंदु को याद करता है जहां यह हुआ था। next()
कॉल next()
एक बार फिर उस बिंदु से निष्पादन फिर से शुरू होगा और अगले yield
विवरण तक जारी रहेगा।
यदि पायथन जेनरेटर फ़ंक्शन के अंत तक बिना किसी अधिक yield
एनकाउंटर तक पहुंचता है, तो एक StopIteration
अपवाद उठाया जाता है (यह सामान्य है, सभी पुनरावृत्तियों उसी तरह व्यवहार करते हैं)।
g3 = function()
a = next(g3) # a becomes 0
b = next(g3) # b becomes 1
c = next(g3) # c becomes 2
...
j = next(g3) # Raises StopIteration, j remains undefined
ध्यान दें कि पायथन 2 जनरेटर वस्तुओं में .next()
विधियाँ हैं जिनका उपयोग मैन्युअल रूप से .next()
मूल्यों के माध्यम से पुनरावृति करने के लिए किया जा सकता है। पायथन 3 में इस विधि को सभी पुनरावृत्तियों के लिए .__next__()
मानक के साथ बदल दिया गया था।
एक जनरेटर को रीसेट करना
याद रखें कि आप केवल एक बार जनरेटर द्वारा उत्पन्न वस्तुओं के माध्यम से पुनरावृति कर सकते हैं। यदि आप पहले से ही एक स्क्रिप्ट में वस्तुओं के माध्यम से पुनरावृत्त कर चुके हैं, तो ऐसा करने का कोई और प्रयास None
करेगा।
यदि आपको एक जनरेटर से उत्पन्न वस्तुओं को एक से अधिक बार उपयोग करने की आवश्यकता है, तो आप या तो जनरेटर फ़ंक्शन को फिर से परिभाषित कर सकते हैं और दूसरी बार इसका उपयोग कर सकते हैं, या, वैकल्पिक रूप से, आप पहले उपयोग पर एक सूची में जनरेटर फ़ंक्शन के आउटपुट को स्टोर कर सकते हैं। यदि आप बड़ी मात्रा में डेटा के साथ काम कर रहे हैं, और सभी डेटा आइटम्स की एक सूची को संग्रहीत करने में बहुत अधिक डिस्क स्थान होगा, तो जनरेटर फ़ंक्शन को फिर से परिभाषित करना एक अच्छा विकल्प होगा। इसके विपरीत, यदि शुरू में वस्तुओं को बनाना महंगा पड़ता है, तो आप एक सूची में उत्पन्न वस्तुओं को संग्रहीत करना पसंद कर सकते हैं ताकि आप उनका पुनः उपयोग कर सकें।
फाइबोनैचि संख्याओं को खोजने के लिए एक जनरेटर का उपयोग करना
एक जनरेटर का व्यावहारिक उपयोग मामला अनंत श्रृंखला के मूल्यों के माध्यम से पुनरावृति करना है। यहाँ फाइबोनैचि अनुक्रम के पहले दस शब्दों को खोजने का एक उदाहरण दिया गया है।
def fib(a=0, b=1):
"""Generator that yields Fibonacci numbers. `a` and `b` are the seed values"""
while True:
yield a
a, b = b, a + b
f = fib()
print(', '.join(str(next(f)) for _ in range(10)))
0, 1, 1, 2, 3, 5, 8, 13, 21, 34
अनंत क्रम
जनरेटर का उपयोग अनंत दृश्यों को दर्शाने के लिए किया जा सकता है:
def integers_starting_from(n):
while True:
yield n
n += 1
natural_numbers = integers_starting_from(1)
उपरोक्त संख्याओं के अनंत अनुक्रम को itertools.count
की मदद से भी उत्पन्न किया जा सकता है। उपरोक्त कोड नीचे लिखा जा सकता है
natural_numbers = itertools.count(1)
आप नए जनरेटर बनाने के लिए अनंत जनरेटर पर जनरेटर की समझ का उपयोग कर सकते हैं:
multiples_of_two = (x * 2 for x in natural_numbers)
multiples_of_three = (x for x in natural_numbers if x % 3 == 0)
ध्यान रखें कि एक अनंत जनरेटर समाप्त नहीं है हो सकता है, इसलिए किसी भी समारोह है कि गंभीर परिणाम होगा जनरेटर पूरी तरह से उपभोग करने का प्रयास करेंगे करने के लिए इसे पारित:
list(multiples_of_two) # will never terminate, or raise an OS-specific error
इसके बजाय, range
साथ सूची / सेट समझ का उपयोग करें (या अजगर के लिए xrange
<3.0):
first_five_multiples_of_three = [next(multiples_of_three) for _ in range(5)]
# [3, 6, 9, 12, 15]
या itertools.islice()
उपयोग itertools.islice()
एक उपसमुच्चय को itertools.islice()
करने के लिए:
from itertools import islice
multiples_of_four = (x * 4 for x in integers_starting_from(1))
first_five_multiples_of_four = list(islice(multiples_of_four, 5))
# [4, 8, 12, 16, 20]
ध्यान दें कि मूल जनरेटर को भी अपडेट किया गया है, ठीक उसी तरह "रूट" से आने वाले अन्य सभी जनरेटर:
next(natural_numbers) # yields 16
next(multiples_of_two) # yields 34
next(multiples_of_four) # yields 24
एक अनंत के दृश्य में भी एक साथ दोहराया जा सकता है for
-loop । सशर्त break
विवरण शामिल करना सुनिश्चित करें ताकि लूप अंततः समाप्त हो जाए:
for idx, number in enumerate(multiplies_of_two):
print(number)
if idx == 9:
break # stop after taking the first 10 multiplies of two
क्लासिक उदाहरण - फाइबोनैचि संख्या
import itertools
def fibonacci():
a, b = 1, 1
while True:
yield a
a, b = b, a + b
first_ten_fibs = list(itertools.islice(fibonacci(), 10))
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
def nth_fib(n):
return next(itertools.islice(fibonacci(), n - 1, n))
ninety_nineth_fib = nth_fib(99) # 354224848179261915075
एक और पुनरावृत्ति से सभी मूल्यों की उपज
यदि आप किसी अन्य पुनरावृत्ति से सभी मान प्राप्त करना चाहते हैं तो yield from
उपयोग करें:
def foob(x):
yield from range(x * 2)
yield from range(2)
list(foob(5)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]
यह जनरेटर के साथ भी काम करता है।
def fibto(n):
a, b = 1, 1
while True:
if a >= n: break
yield a
a, b = b, a + b
def usefib():
yield from fibto(10)
yield from fibto(20)
list(usefib()) # [1, 1, 2, 3, 5, 8, 1, 1, 2, 3, 5, 8, 13]
Coroutines
कोरटाइन को लागू करने के लिए जनरेटर का उपयोग किया जा सकता है:
# create and advance generator to the first yield
def coroutine(func):
def start(*args,**kwargs):
cr = func(*args,**kwargs)
next(cr)
return cr
return start
# example coroutine
@coroutine
def adder(sum = 0):
while True:
x = yield sum
sum += x
# example use
s = adder()
s.send(1) # 1
s.send(2) # 3
आमतौर पर कोराउटीन का उपयोग राज्य मशीनों को लागू करने के लिए किया जाता है, क्योंकि वे मुख्य रूप से एकल-विधि प्रक्रियाओं को बनाने के लिए उपयोगी होते हैं जिन्हें ठीक से काम करने के लिए राज्य की आवश्यकता होती है। वे एक मौजूदा स्थिति पर काम करते हैं और ऑपरेशन पूरा होने पर प्राप्त मूल्य वापस करते हैं।
पुनरावृत्ति के साथ उपज: एक निर्देशिका में सभी फ़ाइलों को पुनरावर्ती रूप से सूचीबद्ध करना
सबसे पहले, पुस्तकालयों को आयात करें जो फाइलों के साथ काम करते हैं:
from os import listdir
from os.path import isfile, join, exists
एक निर्देशिका से केवल फ़ाइलों को पढ़ने के लिए एक सहायक कार्य:
def get_files(path):
for file in listdir(path):
full_path = join(path, file)
if isfile(full_path):
if exists(full_path):
yield full_path
केवल उपनिर्देशिका पाने के लिए एक और सहायक कार्य:
def get_directories(path):
for directory in listdir(path):
full_path = join(path, directory)
if not isfile(full_path):
if exists(full_path):
yield full_path
अब इन कार्यों का उपयोग एक निर्देशिका के भीतर और इसकी सभी उपनिर्देशिकाओं (जनरेटर का उपयोग करके) की सभी फ़ाइलों को पुन: प्राप्त करने के लिए करें:
def get_files_recursive(directory):
for file in get_files(directory):
yield file
for subdirectory in get_directories(directory):
for file in get_files_recursive(subdirectory): # here the recursive call
yield file
इस फ़ंक्शन का उपयोग yield from
सरल किया जा सकता yield from
:
def get_files_recursive(directory):
yield from get_files(directory)
for subdirectory in get_directories(directory):
yield from get_files_recursive(subdirectory)
समानांतर में जनरेटर पर Iterating
समानांतर में कई जनरेटर पर पुनरावृति करने के लिए, zip
बिलिन का उपयोग करें:
for x, y in zip(a,b):
print(x,y)
का परिणाम:
1 x
2 y
3 z
अजगर 2 में आपको इसके बजाय itertools.izip
उपयोग करना चाहिए। यहां हम यह भी देख सकते हैं कि सभी zip
कार्य टुपल्स का उत्पादन करते हैं।
ध्यान दें कि जैसे ही आइट्यूल्स में से कोई एक रन से बाहर निकलेगा, ज़िप चलना बंद कर देगा। यदि आप अधिक से अधिक लंबे समय तक itertools.zip_longest()
, तो itertools.zip_longest()
उपयोग करें।
सूची निर्माण कोड को फिर से तैयार करना
मान लें कि आपके पास एक जटिल कोड है जो एक रिक्त सूची के साथ शुरू करके और बार-बार इसे संलग्न करके एक सूची बनाता है:
def create():
result = []
# logic here...
result.append(value) # possibly in several places
# more logic...
return result # possibly in several places
values = create()
जब यह सूची तर्क के साथ आंतरिक तर्क को बदलने के लिए व्यावहारिक नहीं है, तो आप पूरे फ़ंक्शन को एक जनरेटर-इन-प्लेस में बदल सकते हैं, और फिर परिणाम एकत्र कर सकते हैं:
def create_gen():
# logic...
yield value
# more logic
return # not needed if at the end of the function, of course
values = list(create_gen())
यदि तर्क पुनरावर्ती है, तो पुनरावर्ती कॉल से सभी मूल्यों को "चपटा" परिणाम में शामिल करने के लिए yield from
उपयोग करें:
def preorder_traversal(node):
yield node.value
for child in node.children:
yield from preorder_traversal(child)
खोज कर
next
फ़ंक्शन पुनरावृत्ति के बिना भी उपयोगी है। करने के लिए एक जनरेटर अभिव्यक्ति पासिंग next
एक त्वरित तरीका कुछ विधेय मिलान एक तत्व की पहली घटना के लिए खोज करने के लिए है। प्रक्रियात्मक कोड की तरह
def find_and_transform(sequence, predicate, func):
for element in sequence:
if predicate(element):
return func(element)
raise ValueError
item = find_and_transform(my_sequence, my_predicate, my_func)
के साथ प्रतिस्थापित किया जा सकता है:
item = next(my_func(x) for x in my_sequence if my_predicate(x))
# StopIteration will be raised if there are no matches; this exception can
# be caught and transformed, if desired.
इस उद्देश्य के लिए, अपवाद को बदलने के लिए उपनाम, जैसे कि first = next
, या एक आवरण समारोह बनाना वांछनीय हो सकता है:
def first(generator):
try:
return next(generator)
except StopIteration:
raise ValueError