Сообщить об ошибке.

Итераторы в Python

Большинство объектов - контейнеров можно зациклить, используя инструкцию for ... in:

for element in [1, 2, 3]:
    print(element)

for element in (1, 2, 3):
    print(element)

for key in {'one':1, 'two':2}:
    print(key)

for char in '123':
    print(char)

for line in open('myfile.txt'):
    print(line, end='')

Этот стиль доступа понятен, лаконичен и удобен. Использование итераторов пронизывает и объединяет Python. За кулисами оператор for вызывает функцию iter() для объекта контейнера. Функция возвращает объект итератора, который определяет метод __next__(), который, в свою очередь обращается к элементам в контейнере по одному за раз. Когда нет больше элементов, __next__() возбуждает исключение StopIteration, которое указывает циклу о завершении.

Простой итератор можно создать, применив к последовательности встроенную функцию iter(s). Что бы получить элемент итератора, необходимо вызвать метод __next__() с помощью встроенной функции next().

Пример показывает, как все это работает:

>>> s = 'abc'
>>> it = iter(s)
>>> it
# <iterator object at 0x00A1DB50>
>>> next(it)
# 'a'
>>> next(it)
# 'b'
>>> next(it)
# 'c'
>>> next(it)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#     next(it)
# StopIteration

Обратите внимание, что после использования итератор it остался пустым и его невозможно больше использовать. Итераторы не поддерживают извлечение срезов и получение элемента по индексу. К ним так же нельзя применить функцию len() (узнать длину итерации).

Поведение итератора в классах.

Посмотрев на механику протокола итератора, легко добавить поведение итератора в классы. Для этого, определите метод __iter__(), который возвращает объект с методом __next__(). Если класс определяет метод __next__(), то __iter__() может просто вернуть self:

class Reverse:
    """Итератор для циклического перебора последовательности
        в обратном направлении.
    """
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
#<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
# m
# a
# p
# s