Почему определение __getitem__ в классе делает его итерабельным в python?

Почему определение __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:"

Ответ 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().

Ответ 2

Поддержка итерации для __getitem__ можно рассматривать как "устаревшую функцию", которая допускает более плавный переход, когда PEP234 вводит итерабельность в качестве первичной концепции. Он применим только к классам без __iter__, где __getitem__ принимает целые числа 0, 1, и c и повышает значение IndexError после того, как индекс становится слишком высоким (если вообще когда-либо), как правило, классы "sequence", закодированные до __iter__, появились (хотя ничто не мешает вам также кодировать новые классы).

Лично я бы предпочел не полагаться на это в новом коде, хотя он не устарел и не уходит (отлично работает и в Python 3), так что это всего лишь вопрос стиля и вкуса ( "явный лучше, чем неявный", поэтому я предпочел бы явно поддерживать итерабельность, а не полагаться на __getitem__, поддерживая его неявно для меня, но не большой).

Ответ 3

__getitem__ предшествует протоколу итератора и был в прошлом единственным способом сделать вещи итерабельными. Таким образом, он по-прежнему поддерживается как метод итерации. По сути, протокол для итерации:

  • Проверьте метод __iter__. Если он существует, используйте новый протокол итерации.

  • В противном случае попробуйте вызвать __getitem__ с последовательно большими значениями целочисленного значения до тех пор, пока он не вызовет IndexError.

(2) был единственным способом сделать это, но имел тот недостаток, что он предполагал больше, чем требовалось для поддержки только итерации. Чтобы поддерживать итерацию, вам приходилось поддерживать произвольный доступ, который был намного дороже для таких вещей, как файлы или сетевые потоки, где движение вперед было простым, но для возврата назад потребовалось бы хранить все. __iter__ допускает итерацию без произвольного доступа, но поскольку произвольный доступ обычно позволяет итерацию в любом случае, и, поскольку нарушение обратной совместимости будет плохой, __getitem__ все еще поддерживается.

Ответ 4

Специальные методы, такие как __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.

Ответ 5

Это по историческим причинам. До Python 2.2 __getitem__ был единственным способом создать класс, который можно было бы повторить с циклом for. В 2.2 был добавлен протокол __iter__, но для сохранения обратной совместимости __getitem__ все еще работает для циклов.