मैं इंटरप्ले पर थोड़ा और अधिक प्रकाश डालना चाहता हूं 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__
किसी वस्तु को चलने योग्य बनाने के लिए भी पर्याप्त है