Почему объект без __iter__ и __next__ можно итерировать?
Вопрос для понимания "внутренней кухни" языка. Код следующий:
class NoIter():
def __init__(self):
self.some = [1, 2, 3, 4, 5]
def __getitem__(self, index):
return self.some[index]
t = NoIter()
for item in t:
print(item)
Вывод:
1
2
3
4
5
Вопросы:
- Почему объект можно перебрать, хотя в нём нет
__iter__и__next__? - Почему
__getitem__неявно вызывается во время перебора?
Ответы (2 шт):
Из документации (PEP234):
The two methods correspond to two distinct protocols:
- An object can be iterated over with for if it implements
__iter__()or__getitem__().- An object can function as an iterator if it implements next().
Сводка: для итерации достаточно реализовать любой из этих магических методов.
Если говорить о том, почему это работает, то метод __getitem__ был реализован раньше, чем протокол итерации (методы __iter__, __next__), и раньше этот способ применялся для того, чтобы сделать объект итерируемый (до версии Python ~ 2.2).
Сейчас такой подход является устаревшим и не рекомендуется в применению на практике, но для обратной совместимости остался в языке.
@Павел уже ответил на вопрос, но я добавлю ещё про то, как это работает.
Если в вашем классе есть только метод __getitem__, то Python будет его вызывать, передавая ему индексы начиная с 0 и заканчивая тем, на котором возникнет IndexError. Демонстрация:
>>> class NoIter:
... def __init__(self):
... self.some = [1, 2, 3, 4, 5]
... def __getitem__(self, index):
... print(index) # Выводим переданный индекс
... return self.some[index]
...
>>> t = NoIter()
>>> for item in t:
... pass
...
0
1
2 # Индексы от 0 до 5 (на последнем возникает IndexError)
3
4
5
>>> t[5] # Да, на индексе 5 действительно возникает IndexError
5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __getitem__
IndexError: list index out of range