Почему определение __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
...
Я действительно ожидал бы увидеть ошибку, возвращаемую из "для k в cb:"
6 ответов
Если вы посмотрите на 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 и & и повышает IndexError
как только индекс становится слишком высоким (если вообще когда-либо), как правило, классы "sequence" кодируются ранее __iter__
появился (хотя ничто не мешает вам кодировать новые классы таким же образом).
Лично я бы предпочел не полагаться на это в новом коде, хотя это не считается устаревшим и не исчезает (прекрасно работает и в Python 3), так что это просто вопрос стиля и вкуса ("явное лучше, чем неявное", поэтому Я бы предпочел явно поддерживать итеративность, а не полагаться на __getitem__
поддерживая это безоговорочно для меня - но, не большое).
__getitem__
предшествовал протоколу итератора и в прошлом был единственным способом сделать вещи итеративными. Как таковой, он все еще поддерживается как метод итерации. По сути, протокол для итерации:
Проверьте на
__iter__
метод. Если он существует, используйте новый протокол итерации.В противном случае попробуйте позвонить
__getitem__
с последовательно большими целочисленными значениями, пока он не вызовет IndexError.
(2) раньше был единственный способ сделать это, но имел недостаток в том, что он предполагал больше, чем нужно для поддержки только итерации. Чтобы поддерживать итерацию, вы должны были поддерживать произвольный доступ, который был намного дороже для таких вещей, как файлы или сетевые потоки, где переход вперед был легким, но переход назад потребовал бы хранения всего. __iter__
допускается итерация без произвольного доступа, но так как произвольный доступ обычно допускает итерацию в любом случае, и потому что нарушение обратной совместимости будет плохим, __getitem__
все еще поддерживается.
Специальные методы, такие как __getitem__
добавить специальные поведения для объектов, в том числе итерации.
http://docs.python.org/reference/datamodel.html GetItem
msgstr "для циклов ожидаем, что 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 по этому вопросу.