जवाबों:
अजगर में इटरेटर ऑब्जेक्ट्स इटरेटर प्रोटोकॉल के अनुरूप होते हैं, जिसका मूल अर्थ है कि वे दो तरीके प्रदान करते हैं: __iter__() और __next__()।
__iter__इटरेटर ऑब्जेक्ट और परोक्ष छोरों के शुरू में कहा जाता है।
__next__()विधि अगले मान देता है और परोक्ष प्रत्येक पाश वेतन वृद्धि पर कहा जाता है। जब वापसी करने के लिए अधिक मूल्य नहीं होते हैं, तो इसे रोकना बंद कर दिया जाता है।
यहाँ एक काउंटर का सरल उदाहरण दिया गया है:
class Counter:
def __init__(self, low, high):
self.current = low - 1
self.high = high
def __iter__(self):
return self
def __next__(self): # Python 2: def next(self)
self.current += 1
if self.current < self.high:
return self.current
raise StopIteration
for c in Counter(3, 9):
print(c)
यह प्रिंट करेगा:
3
4
5
6
7
8
जनरेटर का उपयोग करके लिखना आसान है, जैसा कि पिछले उत्तर में कवर किया गया है:
def counter(low, high):
current = low
while current < high:
yield current
current += 1
for c in counter(3, 9):
print(c)
मुद्रित आउटपुट समान होगा। हुड के तहत, जनरेटर ऑब्जेक्ट पुनरावृत्त प्रोटोकॉल का समर्थन करता है और क्लास काउंटर के समान कुछ करता है।
डेविड मर्टज़ का लेख, Iterators और Simple Generators , एक बहुत अच्छा परिचय है।
__next__। counterएक पुनरावृत्ति है, लेकिन यह एक अनुक्रम नहीं है। यह अपने मूल्यों को संग्रहीत नहीं करता है। उदाहरण के लिए, आपको दोहरे-नेस्टेड-लूप में काउंटर का उपयोग नहीं करना चाहिए।
__iter__(इसके अलावा __init__) में असाइन किया जाना चाहिए । अन्यथा, ऑब्जेक्ट केवल एक बार पुनरावृत्त हो सकता है। उदाहरण के लिए, यदि आप कहते हैं ctr = Counters(3, 8), तो आप for c in ctrएक से अधिक बार उपयोग नहीं कर सकते ।
Counterएक पुनरावृत्ति है, और पुनरावृत्तियों केवल एक बार पुनरावृत्त होना चाहिए। यदि आप रीसेट करते self.currentहैं __iter__, तो एक नेस्टेड लूप Counterपूरी तरह से टूट जाएगा, और पुनरावृत्तियों के सभी प्रकार के व्यवहार (कि iterउन पर कॉल करना बेकार है) का उल्लंघन किया जाता है। यदि आप ctrएक से अधिक बार पुनरावृति करने में सक्षम होना चाहते हैं , तो इसे एक गैर- पुनरावृत्त पुनरावृत्त होने की आवश्यकता है, जहां यह हर बार __iter__लागू होने पर एक नया पुनरावृत्ति देता है। मिक्स एंड मैच करने की कोशिश (एक इटरेटर जो कि जब __iter__आह्वान किया जाता है तब इसे रीसेट किया जाता है) प्रोटोकॉल का उल्लंघन करता है।
Counterएक गैर-पुनरावृत्त पुनरावृत्ति होना था , तो आप __next__/ nextपूरी तरह से परिभाषा को हटा देंगे , और संभवतः __iter__इस उत्तर के अंत में वर्णित जनरेटर के रूप में उसी रूप के जनरेटर फ़ंक्शन के रूप में फिर से परिभाषित करें (सीमा के बजाय को छोड़कर) तर्कों से आने के बाद __iter__, वे दलीलों को __init__सहेजने selfऔर उन पर पहुंचने से बचते selfहैं __iter__)।
पुनरावृत्त समारोह बनाने के चार तरीके हैं:
__iter__और__next__ (या nextपायथन 2.x में))__getitem__ )उदाहरण:
# generator
def uc_gen(text):
for char in text.upper():
yield char
# generator expression
def uc_genexp(text):
return (char for char in text.upper())
# iterator protocol
class uc_iter():
def __init__(self, text):
self.text = text.upper()
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
result = self.text[self.index]
except IndexError:
raise StopIteration
self.index += 1
return result
# getitem method
class uc_getitem():
def __init__(self, text):
self.text = text.upper()
def __getitem__(self, index):
return self.text[index]
कार्रवाई में सभी चार तरीकों को देखने के लिए:
for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
for ch in iterator('abcde'):
print(ch, end=' ')
print()
जिसके परिणामस्वरूप:
A B C D E
A B C D E
A B C D E
A B C D E
नोट :
दो जनरेटर प्रकार ( uc_genऔर uc_genexp) नहीं हो सकते हैं reversed(); प्लेन इट्रेटर ( uc_iter) को __reversed__मैजिक मेथड की आवश्यकता होगी (जो डॉक्स के अनुसार , एक नया इटरेटर अवश्य लौटाए, लेकिन लौटाने के selfकाम (कम से कम सीपीथॉन में)); और गेटिटेम चलने योग्य ( uc_getitem) में __len__जादू की विधि होनी चाहिए :
# for uc_iter we add __reversed__ and update __next__
def __reversed__(self):
self.index = -1
return self
def __next__(self):
try:
result = self.text[self.index]
except IndexError:
raise StopIteration
self.index += -1 if self.index < 0 else +1
return result
# for uc_getitem
def __len__(self)
return len(self.text)
कर्नल पैनिक के अनन्त आलसी मूल्यांकन के बारे में माध्यमिक प्रश्न का उत्तर देने के लिए, ऊपर दिए गए चार तरीकों में से प्रत्येक का उपयोग करते हुए, यहां वे उदाहरण दिए गए हैं:
# generator
def even_gen():
result = 0
while True:
yield result
result += 2
# generator expression
def even_genexp():
return (num for num in even_gen()) # or even_iter or even_getitem
# not much value under these circumstances
# iterator protocol
class even_iter():
def __init__(self):
self.value = 0
def __iter__(self):
return self
def __next__(self):
next_value = self.value
self.value += 2
return next_value
# getitem method
class even_getitem():
def __getitem__(self, index):
return index * 2
import random
for iterator in even_gen, even_genexp, even_iter, even_getitem:
limit = random.randint(15, 30)
count = 0
for even in iterator():
print even,
count += 1
if count >= limit:
break
print
जिसके परिणामस्वरूप (कम से कम मेरे सैंपल रन के लिए):
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
कैसे चुनें कि किसका उपयोग करना है? यह ज्यादातर स्वाद का मामला है। जिन दो तरीकों को मैं सबसे अधिक बार देखता हूं, वे हैं जनरेटर और इटरेटर प्रोटोकॉल, साथ ही एक हाइब्रिड ( __iter__एक जनरेटर की वापसी)।
जेनरेटर अभिव्यक्तियाँ सूची बोध को बदलने के लिए उपयोगी हैं (वे आलसी हैं और इसलिए संसाधनों पर बचत कर सकते हैं)।
यदि किसी को पहले पायथन 2.x संस्करणों के साथ संगतता की आवश्यकता होती है __getitem__।
uc_iterहोने पर एक उदाहरण समाप्त हो जाना चाहिए (अन्यथा यह अनंत द्वारा होगा); यदि आप इसे फिर से करना चाहते हैं तो आपको फिर से कॉल करके एक नया पुनरावृत्ति प्राप्त uc_iter()करना होगा।
self.index = 0में __iter__है, ताकि आप में कई बार पुनरावृति कर सकते हैं। अन्यथा आप नहीं कर सकते।
सबसे पहले सभी इटर्टूल मॉड्यूल उन सभी प्रकार के मामलों के लिए अविश्वसनीय रूप से उपयोगी होते हैं, जिनमें एक इटरेटर उपयोगी होगा, लेकिन यहाँ आपको अजगर में एक इटरेटर बनाने की आवश्यकता है:
प्राप्ति
क्या यह अच्छा नहीं है? किसी फ़ंक्शन में सामान्य रिटर्न को बदलने के लिए यील्ड का उपयोग किया जा सकता है । यह वस्तु को उसी तरह लौटाता है, लेकिन राज्य को नष्ट करने और बाहर निकलने के बजाय, जब आप अगली पुनरावृत्ति को निष्पादित करना चाहते हैं, तो यह राज्य को बचाता है। यहाँ इसका एक उदाहरण है इटर्टलस फ़ंक्शन सूची से सीधे खींची गई क्रिया :
def count(n=0):
while True:
yield n
n += 1
जैसा कि फ़ंक्शन विवरण में कहा गया है (यह इटर्टल्स मॉड्यूल ... से गिनती () फ़ंक्शन है), यह एक इट्रेटर पैदा करता है जो एन के साथ शुरू होने वाले लगातार पूर्णांक देता है।
जेनरेटर के भाव कीड़े (भयानक कीड़े!) की एक पूरी दूसरी कैन हैं। स्मृति को सहेजने के लिए सूची बोध के स्थान पर उनका उपयोग किया जा सकता है (सूची बोधक स्मृति में एक सूची बनाते हैं जो उपयोग के बाद नष्ट हो जाती है यदि एक चर को नहीं सौंपा गया है, लेकिन जनरेटर के भाव एक जेनरेटर ऑब्जेक्ट बना सकते हैं ... जो कि फैंसी तरीका है Iterator कह रहा है)। यहाँ एक जनरेटर अभिव्यक्ति परिभाषा का एक उदाहरण है:
gen = (n for n in xrange(0,11))
यह पूरी तरह से 0 और 10 के बीच होने के अलावा पूर्व सीमा को छोड़कर हमारी इटेरेटर परिभाषा के समान है।
मैंने अभी-अभी xrange () पाया ( इससे पहले मैंने इसे नहीं देखा था ...) और इसे उपरोक्त उदाहरण में जोड़ा। xrange () रेंज का एक पुनरावृत्त संस्करण है () जिसमें सूची का पुनर्निर्माण नहीं करने का लाभ है। यह बहुत उपयोगी होगा यदि आपके पास डेटा का एक विशाल कोष इसे पुनरावृत्त करने के लिए है और केवल इतना मेमोरी है कि इसे करना है।
मैं आप में से कुछ कर देखने return selfमें __iter__। मैं सिर्फ यह नोट करना चाहता था कि __iter__खुद एक जनरेटर हो सकता है (इस प्रकार अपवाद की आवश्यकता को दूर करना __next__और बढ़ाना StopIteration)
class range:
def __init__(self,a,b):
self.a = a
self.b = b
def __iter__(self):
i = self.a
while i < self.b:
yield i
i+=1
बेशक यहाँ एक सीधे तौर पर एक जनरेटर बना सकता है, लेकिन अधिक जटिल वर्गों के लिए यह उपयोगी हो सकता है।
return selfमें __iter__। जब मैं इसका उपयोग करने की कोशिश करने जा रहा था yieldतो मैंने पाया कि आपका कोड वही है जो मैं कोशिश करना चाहता हूं।
next()? return iter(self).next()?
self.currentया किसी अन्य काउंटर का ट्रैक रखने में शामिल नहीं है । यह शीर्ष मतदान का जवाब होना चाहिए!
iter, लेकिन वे स्वयं कक्षा के उदाहरण नहीं होते हैं।
यह सवाल पुनरावृत्ति वाली वस्तुओं के बारे में है, न कि पुनरावृत्तियों के बारे में। पायथन में, अनुक्रम भी पुनरावृत्त होते हैं, इसलिए एक पुनरावृत्त वर्ग बनाने का एक तरीका यह है कि इसे एक अनुक्रम की तरह व्यवहार किया जाए, अर्थात इसे __getitem__और __len__तरीके दें। मैंने पायथन 2 और 3 पर इसका परीक्षण किया है।
class CustomRange:
def __init__(self, low, high):
self.low = low
self.high = high
def __getitem__(self, item):
if item >= len(self):
raise IndexError("CustomRange index out of range")
return self.low + item
def __len__(self):
return self.high - self.low
cr = CustomRange(0, 10)
for i in cr:
print(i)
__len__()तरीका नहीं है। __getitem__अकेले के साथ अपेक्षित व्यवहार पर्याप्त है।
इस पृष्ठ पर सभी उत्तर वास्तव में एक जटिल वस्तु के लिए महान हैं। लेकिन गुणों के रूप में iterable प्रकार निर्मित युक्त उन लोगों के लिए, की तरह str, list, setया dict, या के किसी भी कार्यान्वयन collections.Iterable, आप कुछ चीजें अपनी कक्षा में छोड़ सकते हैं।
class Test(object):
def __init__(self, string):
self.string = string
def __iter__(self):
# since your string is already iterable
return (ch for ch in self.string)
# or simply
return self.string.__iter__()
# also
return iter(self.string)
इसका उपयोग इस तरह किया जा सकता है:
for x in Test("abcde"):
print(x)
# prints
# a
# b
# c
# d
# e
return iter(self.string):।
यह बिना चलने वाला कार्य है yield। यह iterफ़ंक्शन और एक क्लोजर का उपयोग करता है जो इसे listअजगर 2 के लिए संलग्न करने के दायरे में एक उत्परिवर्तनीय ( ) स्थिति में रखता है ।
def count(low, high):
counter = [0]
def tmp():
val = low + counter[0]
if val < high:
counter[0] += 1
return val
return None
return iter(tmp, None)
पायथन 3 के लिए, क्लोजर स्टेट को एनक्लोजिंग स्कोप में अपरिवर्तनीय रखा जाता है और nonlocalस्टेट वेरिएबल को अपडेट करने के लिए लोकल स्कोप में उपयोग किया जाता है।
def count(low, high):
counter = 0
def tmp():
nonlocal counter
val = low + counter
if val < high:
counter += 1
return val
return None
return iter(tmp, None)
परीक्षा;
for i in count(1,10):
print(i)
1
2
3
4
5
6
7
8
9
iter, लेकिन सिर्फ स्पष्ट होने के लिए: यह एक yieldआधारित जनरेटर फ़ंक्शन का उपयोग करने की तुलना में अधिक जटिल और कम कुशल है ; पायथन में yieldआधारित जनरेटर फ़ंक्शंस के लिए एक टन इंटरप्रेटर समर्थन है जो आप यहाँ का लाभ नहीं उठा सकते हैं, जिससे यह कोड काफी धीमा हो जाता है। फिर भी गैर-मतदान किया।
यदि आप कुछ छोटा और सरल खोज रहे हैं, तो शायद यह आपके लिए पर्याप्त होगा:
class A(object):
def __init__(self, l):
self.data = l
def __iter__(self):
return iter(self.data)
उपयोग का उदाहरण:
In [3]: a = A([2,3,4])
In [4]: [i for i in a]
Out[4]: [2, 3, 4]
मैट ग्रेगोरी के जवाब से प्रेरित एक और अधिक जटिल पुनरावृत्ति है जो ए, बी, ..., जेड, आ, एबी, ..., ज़ज़, आआ, आब, ..., ज़ज़ी, ज़ज़ लौट आएगा।
class AlphaCounter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self): # Python 3: def __next__(self)
alpha = ' abcdefghijklmnopqrstuvwxyz'
n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))])
n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))])
if n_current > n_high:
raise StopIteration
else:
increment = True
ret = ''
for x in self.current[::-1]:
if 'z' == x:
if increment:
ret += 'a'
else:
ret += 'z'
else:
if increment:
ret += alpha[alpha.find(x)+1]
increment = False
else:
ret += x
if increment:
ret += 'a'
tmp = self.current
self.current = ret[::-1]
return tmp
for c in AlphaCounter('a', 'zzz'):
print(c)