Ответ 1
Если вы посмотрите PEP234, определяющий итераторы, он говорит:
1. An object can be iterated over with "for" if it implements
__iter__() or __getitem__().
2. An object can function as an iterator if it implements next().
Почему определение __getitem__ в классе делает его итерабельным?
Например, если я пишу:
class b:
def __getitem__(self, k):
return k
cb = b()
for k in cb:
print k
Я получаю вывод:
0
1
2
3
4
5
6
7
8
...
Я бы действительно ожидал увидеть ошибку, возвращенную из "for k in cb:"
Если вы посмотрите PEP234, определяющий итераторы, он говорит:
1. An object can be iterated over with "for" if it implements
__iter__() or __getitem__().
2. An object can function as an iterator if it implements next().
Поддержка итерации для __getitem__
можно рассматривать как "устаревшую функцию", которая допускает более плавный переход, когда PEP234 вводит итерабельность в качестве первичной концепции. Он применим только к классам без __iter__
, где __getitem__
принимает целые числа 0, 1, и c и повышает значение IndexError
после того, как индекс становится слишком высоким (если вообще когда-либо), как правило, классы "sequence", закодированные до __iter__
, появились (хотя ничто не мешает вам также кодировать новые классы).
Лично я бы предпочел не полагаться на это в новом коде, хотя он не устарел и не уходит (отлично работает и в Python 3), так что это всего лишь вопрос стиля и вкуса ( "явный лучше, чем неявный", поэтому я предпочел бы явно поддерживать итерабельность, а не полагаться на __getitem__
, поддерживая его неявно для меня, но не большой).
__getitem__
предшествует протоколу итератора и был в прошлом единственным способом сделать вещи итерабельными. Таким образом, он по-прежнему поддерживается как метод итерации. По сути, протокол для итерации:
Проверьте метод __iter__
. Если он существует, используйте новый протокол итерации.
В противном случае попробуйте вызвать __getitem__
с последовательно большими значениями целочисленного значения до тех пор, пока он не вызовет IndexError.
(2) был единственным способом сделать это, но имел тот недостаток, что он предполагал больше, чем требовалось для поддержки только итерации. Чтобы поддерживать итерацию, вам приходилось поддерживать произвольный доступ, который был намного дороже для таких вещей, как файлы или сетевые потоки, где движение вперед было простым, но для возврата назад потребовалось бы хранить все. __iter__
допускает итерацию без произвольного доступа, но поскольку произвольный доступ обычно позволяет итерацию в любом случае, и, поскольку нарушение обратной совместимости будет плохой, __getitem__
все еще поддерживается.
Специальные методы, такие как __getitem__
добавить специальные типы поведения к объектам, включая итерацию.
http://docs.python.org/reference/datamodel.html#object. getitem p >
"для циклов ожидают, что IndexError будет поднят для незаконных индексов, чтобы обеспечить надлежащее обнаружение конца последовательности."
Поднимите IndexError, чтобы сигнализировать о конце последовательности.
Ваш код в основном эквивалентен:
i = 0
while True:
try:
yield object[i]
i += 1
except IndexError:
break
Где объект - это то, что вы повторяете в цикле for.
Это по историческим причинам. До Python 2.2 __getitem__ был единственным способом создать класс, который можно было бы повторить с циклом for. В 2.2 был добавлен протокол __iter__, но для сохранения обратной совместимости __getitem__ все еще работает для циклов.
Потому что cb[0]
совпадает с cb.__getitem__(0)
. См. документацию python по этому вопросу.