Iterator

Паттерн обхода: перебрать элементы коллекции, не зная и не раскрывая, как она устроена внутри.

Iterator предоставляет способ последовательного доступа к элементам коллекции, не раскрывая её внутреннее представление.

Какую задачу решает

Список, дерево и связный список устроены по-разному, но перебирать их хочется одинаково: for x in collection. Iterator отделяет обход от хранения: коллекция отдаёт итератор, который знает, как двигаться по ней, а клиент работает с единым протоколом. В Python этот паттерн встроен в язык.

Идея и реализация

Протокол итератора в Python — методы __iter__ (вернуть итератор) и __next__ (вернуть следующий элемент или бросить StopIteration). Сделаем коллекцию, обходящую элементы в обратном порядке.

class Reversed:
    def __init__(self, data):
        self._data = data

    def __iter__(self):
        self._i = len(self._data)
        return self

    def __next__(self):
        if self._i == 0:
            raise StopIteration
        self._i -= 1
        return self._data[self._i]


for item in Reversed(["a", "b", "c"]):
    print(item)

Вывод:

c
b
a

Цикл for сам вызывает __iter__, затем многократно __next__, пока не поймает StopIteration. Клиент не знает, что внутри список и что обход идёт с конца — он просто перебирает. Любую структуру (дерево, граф) можно сделать перебираемой так же.

Питоничный способ: генераторы

В Python почти всегда вместо ручных __next__ пишут генератор с yield — это и есть итератор, созданный компактно.

def countdown(n):
    while n > 0:
        yield n          # лениво отдаём значения по одному
        n -= 1


print(list(countdown(3)))
print(sum(countdown(5)))

Вывод:

[3, 2, 1]
15

Генератор ленив: значения вычисляются по мере запроса, что позволяет работать с бесконечными или огромными последовательностями без загрузки всего в память.

Где встречается

Везде в Python: for, распаковка, map/filter, itertools, чтение файла построчно. В других языках — Iterator/IEnumerable, обход коллекций, потоки данных, пагинация API (следующий итератор подгружает следующую страницу).

Итог

  • Iterator отделяет обход от хранения, давая единый протокол перебора.
  • В Python — это __iter__/__next__ и StopIteration.
  • Генераторы с yield — самый компактный способ создать итератор.
Проверьте себя
1. Что отделяет Iterator?
AСоздание от копирования
BОбход коллекции от её внутреннего устройства
CОтправителя от исполнителя
DАбстракцию от реализации
2. Какие методы образуют протокол итератора в Python?
Astart и stop
B__iter__ и __next__ (с StopIteration в конце)
Cexecute и undo
Dsubscribe и notify
3. Чем удобен генератор с yield?
AОн всегда быстрее списка
BОн лениво отдаёт значения, экономя память на больших/бесконечных последовательностях
CОн гарантирует один экземпляр
DОн копирует коллекцию
Поддержать проект