मैं इंटरप्ले पर थोड़ा और अधिक प्रकाश डालना चाहता हूं iter, __iter__और __getitem__पर्दे के पीछे क्या होता है। उस ज्ञान से लैस, आप यह समझने में सक्षम होंगे कि आप सबसे अच्छा क्यों कर सकते हैं
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
मैं पहले तथ्यों को सूचीबद्ध करूंगा और फिर एक त्वरित अनुस्मारक के साथ पालन करूंगा कि जब आप नौकरी करते हैं तो क्या होता है for अजगर में लूप , तो तथ्यों को चित्रित करने के लिए चर्चा के बाद।
तथ्य
आप oकॉल करके किसी भी ऑब्जेक्ट से एक iterator प्राप्त कर सकते हैं iter(o)यदि निम्न में से कम से कम एक स्थिति सही है:
a) के oपास एक __iter__विधि है जो इट्रेटर ऑब्जेक्ट लौटाता है। एक पुनरावृत्त एक __iter__और __next__(पायथन 2:) nextविधि के साथ कोई भी वस्तु है ।
b) oकी एक __getitem__विधि है।
उदाहरण के लिए Iterableया Sequenceविशेषता के लिए जाँच करना __iter__पर्याप्त नहीं है।
यदि कोई वस्तु oकेवल लागू होती है __getitem__, लेकिन नहीं __iter__, iter(o)तो एक पुनरावृत्ति का निर्माण होगा जो oपूर्णांक सूचकांक से आइटम लाने की कोशिश करता है , सूचकांक 0. पर शुरू होता है। इट्रेटर किसी भी IndexError(लेकिन कोई अन्य त्रुटियों) को नहीं उठाएगा और फिर StopIterationखुद को उठाता है।
सबसे सामान्य अर्थों में, यह जांचने का कोई तरीका नहीं है कि क्या इट्रेटर द्वारा वापस लौटाया गया iterहै या नहीं।
यदि कोई ऑब्जेक्ट oलागू होता है __iter__, तो iterफ़ंक्शन यह सुनिश्चित करेगा कि ऑब्जेक्ट द्वारा लौटाया गया __iter__एक पुनरावृत्ति है। यदि कोई वस्तु केवल लागू होती है तो कोई पवित्रता जांच नहीं है __getitem__।
__iter__जीतता है। यदि कोई ऑब्जेक्ट oदोनों को लागू करता है __iter__और __getitem__, iter(o)कॉल करेगा __iter__।
यदि आप अपनी खुद की वस्तुओं को चलने योग्य बनाना चाहते हैं, तो हमेशा __iter__विधि को लागू करें ।
for छोरों
साथ पालन करने के लिए, आपको यह समझने की आवश्यकता है कि जब आप forपायथन में एक लूप को नियुक्त करते हैं तो क्या होता है । यदि आप पहले से ही जानते हैं तो अगले भाग पर सीधे जाने के लिए स्वतंत्र महसूस करें।
जब आप for item in oकिसी पुनरावृत्त वस्तु के लिए उपयोग करते हैं o, तो पायथन कॉल करता है iter(o)और वापसी मूल्य के रूप में एक पुनरावृत्त वस्तु की अपेक्षा करता है। एक पुनरावृत्त कोई वस्तु है जो __next__(या nextपायथन 2 में) विधि और एक __iter__विधि को लागू करता है।
कन्वेंशन द्वारा, __iter__एक पुनरावृत्ति की विधि को ऑब्जेक्ट को स्वयं (यानी return self) वापस करना चाहिए । अजगर तब उठने nextतक इटरेटर पर कॉल करता StopIterationहै। यह सब स्पष्ट रूप से होता है, लेकिन निम्नलिखित प्रदर्शन इसे दिखाई देते हैं:
import random
class DemoIterable(object):
def __iter__(self):
print('__iter__ called')
return DemoIterator()
class DemoIterator(object):
def __iter__(self):
return self
def __next__(self):
print('__next__ called')
r = random.randint(1, 10)
if r == 5:
print('raising StopIteration')
raise StopIteration
return r
इस पर परिवर्तन DemoIterable:
>>> di = DemoIterable()
>>> for x in di:
... print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
चर्चा और चित्र
बिंदु 1 और 2 पर: एक पुनरावृत्ति और अविश्वसनीय चेक प्राप्त करना
निम्नलिखित वर्ग पर विचार करें:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
iterउदाहरण के साथ कॉल करना BasicIterableकिसी भी समस्या के बिना एक पुनरावृत्ति लौटाएगा क्योंकि BasicIterableलागू करता है __getitem__।
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
हालांकि, यह नोट करना महत्वपूर्ण है कि bनहीं है __iter__विशेषता और का एक उदाहरण नहीं माना जाता है Iterableया Sequence:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
यही कारण है कि लुसियानो रामलहो द्वारा फ्लुएंट पायथन कॉल करने iterऔर संभावित TypeErrorको संभालने के लिए सबसे सटीक तरीका है जो यह जांचने की सलाह देता है कि क्या कोई ऑब्जेक्ट चलने योग्य है। पुस्तक से सीधे उद्धरण:
पायथन 3.4 के रूप में, यह जांचने का सबसे सटीक तरीका है कि कोई ऑब्जेक्ट xचलने योग्य है या नहीं, अपवाद को कॉल करने iter(x)और संभालने के लिए TypeError। यह उपयोग करने की तुलना में अधिक सटीक है isinstance(x, abc.Iterable), क्योंकि iter(x)विरासत __getitem__विधि पर भी विचार Iterableकरता है , जबकि एबीसी नहीं करता है।
बिंदु 3 पर: केवल वस्तुओं को प्रदान करने पर जोर देना __getitem__, लेकिन नहीं__iter__
BasicIterableअपेक्षा के अनुसार काम करने की एक आवृत्ति से अधिक : पायथन एक पुनरावृत्ति का निर्माण करता है जो सूचकांक द्वारा आइटम लाने की कोशिश करता है, शून्य से शुरू होता है, जब तक कि एक IndexErrorउठाया नहीं जाता है। डेमो ऑब्जेक्ट की __getitem__विधि केवल वह लौटाती है itemजिसे __getitem__(self, item)इट्रेटर द्वारा तर्क के रूप में आपूर्ति की गई थी iter।
>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
ध्यान दें कि StopIterationजब यह अगले आइटम को वापस नहीं कर सकता है और IndexErrorजो item == 3आंतरिक रूप से संभाला जाता है, उसके लिए इट्रेटर उठाता है । यही कारण है कि BasicIterableएक forपाश के साथ पाशन उम्मीद के मुताबिक काम करता है:
>>> for x in b:
... print(x)
...
0
1
2
यहाँ एक और उदाहरण दिया गया है कि घर की अवधारणा को चलाने के लिए कि कैसे पुनरावृत्ती iterसूचकांक द्वारा वस्तुओं तक पहुँचने की कोशिश करती है। WrappedDictसे वारिस नहीं है dict, जिसका अर्थ है उदाहरणों में एक __iter__विधि नहीं होगी ।
class WrappedDict(object): # note: no inheritance from dict!
def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):
try:
return self._dict[item] # delegate to dict.__getitem__
except KeyError:
raise IndexError
ध्यान दें कि कॉल के लिए __getitem__प्रत्यायोजित किया जाता dict.__getitem__है जिसके लिए वर्ग ब्रैकेट अंकन केवल एक आशुलिपि है।
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
बिंदु 4 और 5 पर: iterकॉल करने पर एक पुनरावृत्ति के लिए जाँच__iter__ :
जब iter(o)किसी ऑब्जेक्ट के लिए कॉल किया जाता है o, iterतो यह सुनिश्चित कर देगा कि __iter__यदि विधि मौजूद है, तो रिटर्न वैल्यू , इट्रेटर है। इसका मतलब है कि लौटी हुई वस्तु को लागू करना चाहिए __next__(या nextपायथन 2 में) और __iter__। iterकेवल वस्तुओं को प्रदान करने के लिए कोई संन्यास की जांच नहीं कर सकता है __getitem__, क्योंकि यह जांचने का कोई तरीका नहीं है कि वस्तु के आइटम पूर्णांक सूचकांक द्वारा सुलभ हैं या नहीं।
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
ध्यान दें कि FailIterIterableइंस्टेंसेस से एक इटरेटर का निर्माण तुरंत विफल हो जाता है, जबकि एक इटरेटर का निर्माण FailGetItemIterableसफल होता है, लेकिन पहली कॉल पर एक अपवाद को फेंक देगा __next__।
>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/iterdemo.py", line 42, in __getitem__
raise Exception
Exception
बिंदु 6 पर: __iter__जीतता है
यह एक सीधा है। यदि कोई ऑब्जेक्ट लागू होता है __iter__और __getitem__, iterकॉल करेगा __iter__। निम्नलिखित वर्ग पर विचार करें
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
और उत्पादन जब एक उदाहरण पर पाशन:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
बिंदु 7 पर: आपके चलने योग्य वर्गों को लागू करना चाहिए __iter__
आप अपने आप से पूछ सकते हैं कि अधिकांश बिलियन अनुक्रम listएक __iter__विधि को लागू करने के लिए क्यों __getitem__पर्याप्त होंगे।
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
सब के बाद, यात्रा ऊपर है, जो प्रतिनिधियों कॉल करने के लिए वर्ग के उदाहरण से अधिक __getitem__करने के लिए list.__getitem__(वर्ग कोष्ठक अंकन का उपयोग), ठीक से काम करेगा:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
आपके कस्टम पुनरावृत्तियों को लागू करने के कारण __iter__इस प्रकार हैं:
- यदि आप लागू करते हैं
__iter__, तो इंस्टेंसेस को पुनरावृत्त माना isinstance(o, collections.abc.Iterable)जाएगा , और वापस आ जाएगा True।
- यदि द्वारा दी गई वस्तु
__iter__एक पुनरावृत्ति नहीं है, iterतो तुरंत विफल हो जाएगी और ए बढ़ाएगी TypeError।
__getitem__पश्चगामी संगतता कारणों के लिए विशेष हैंडलिंग मौजूद है। धाराप्रवाह पायथन से फिर से उद्धृत:
यही कारण है कि किसी भी पायथन अनुक्रम चलने योग्य है: वे सभी लागू करते हैं __getitem__। वास्तव में, मानक क्रम भी लागू होता है __iter__, और आपका भी होना चाहिए, क्योंकि __getitem__पिछड़े संगतता कारणों के लिए विशेष हैंडलिंग मौजूद है और भविष्य में चली जा सकती है (हालांकि यह जैसा कि मैं इसे लिखता हूं, यह पदावनत नहीं है)।
__getitem__किसी वस्तु को चलने योग्य बनाने के लिए भी पर्याप्त है