इटरेशन प्रोटोकॉल और जेनरेटर में महारत हासिल करें, ताकि डेटा स्ट्रीम को स्थिर मेमोरी में, आलसी ढंग से प्रोसेस किया जा सके।
इस पाठ को Kodokon में खोलेंPython का for एक प्रोटोकॉल से ज़्यादा कुछ नहीं है: iter(obj) एक इटरेटर माँगता है, फिर StopIteration अपवाद आने तक next() कॉल होता रहता है। इटरेबल (वह लिस्ट, जो हर बार एक ताज़ा इटरेटर सौंप सकती है) और इटरेटर (वह कर्सर, जिसे केवल एक बार पार किया जा सकता है) में अंतर करना न भूलें। इस प्रोटोकॉल को समझने का मतलब है यह समझना कि कुछ ऑब्जेक्ट "खप क्यों जाते हैं।"
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
print(next(iterator))
# next(iterator) would raise StopIterationजिस फ़ंक्शन में yield होता है, वह कॉल किए जाने पर चलता नहीं है: वह एक जेनरेटर लौटाता है, यानी ऐसा इटरेटर जिसका मुख्य भाग माँग पर चलता है। हर next() अगले yield तक आगे बढ़ता है, जहाँ निष्पादन अपनी पूरी लोकल स्टेट सहित निलंबित हो जाता है। इसका सीधा नतीजा: स्ट्रीम का आकार चाहे जो हो, मेमोरी स्थिर बनी रहती है - आप पूरी लिस्ट साकार करने के बजाय वैल्यू एक-एक करके उत्पन्न करते हैं।
def squares(limit):
n = 0
while n < limit:
yield n * n
n += 1
gen = squares(4)
print(next(gen)) # 0
print(list(gen)) # [1, 4, 9]प्रोडक्शन में मुख्य लाभ: पाइपलाइन। हर चरण पिछले चरण की स्ट्रीम को खपाता है और आलसी ढंग से अपनी खुद की स्ट्रीम उत्पन्न करता है; अंतिम खपत (list(), sum(), कोई for) से पहले कुछ भी नहीं चलता। ध्यान में रखने लायक एक समझौता: किसी लिस्ट को कई बार पार किया जा सकता है और स्लाइस किया जा सकता है, जेनरेटर को नहीं; दूसरी ओर, जेनरेटर 10 GB की फ़ाइल को RAM भरे बिना प्रोसेस कर लेता है। जेनरेटर एक्सप्रेशन - (f(x) for x in xs if cond) - बिना कोई फ़ंक्शन परिभाषित किए सरल चरणों को संभाल लेते हैं।
lines = [" 12 ", "x", " 7", "", "30"]
def clean(rows):
for row in rows:
row = row.strip()
if row.isdigit():
yield int(row)
def keep_from(values, floor):
return (v for v in values if v >= floor)
pipeline = keep_from(clean(lines), 10)
print(list(pipeline)) # [12, 30]