Итераторы в Python

Итераторы в Python: протокол __iter__/__next__, собственные итераторы, функции iter()/next(), отличие итерируемого объекта от итератора.

Итератор — объект, реализующий два метода: __iter__() (возвращает самого себя) и __next__() (возвращает следующий элемент или поднимает StopIteration). Это называется протоколом итератора.

Итерируемое vs итератор

Важно разграничить два понятия:

ПонятиеЧто этоПримеры
Итерируемое (iterable)объект с методом __iter__(), возвращающим итераторlist, str, tuple, dict
Итератор (iterator)объект с __iter__() и __next__()enumerate, zip, map, объект-генератор
numbers = [1, 2, 3]          # итерируемое (list)

it = iter(numbers)           # получаем итератор из списка
print(type(it))

print(next(it))              # 1
print(next(it))              # 2
print(next(it))              # 3
# next(it)                   # StopIteration — элементы кончились

Вывод:

<class 'list_iterator'>
1
2
3

Список порождает итератор при вызове iter(), но сам итератором не является: его можно перебирать снова и снова, а итератор — только один раз.

Как работает цикл for под капотом

# Цикл for делает примерно следующее:
items = ["a", "b", "c"]

it = iter(items)             # 1. получить итератор
while True:
    try:
        item = next(it)      # 2. взять следующий элемент
        print(item)
    except StopIteration:    # 3. итератор исчерпан — выйти
        break

Вывод:

a
b
c

Создаём собственный итератор

Напишем итератор, возвращающий квадраты чисел от 1 до n:

class Squares:
    """Итератор квадратов: 1², 2², 3², ..., n²"""

    def __init__(self, n):
        self.n = n
        self.current = 0

    def __iter__(self):
        return self          # итератор возвращает самого себя

    def __next__(self):
        self.current += 1
        if self.current > self.n:
            raise StopIteration
        return self.current ** 2


for sq in Squares(5):
    print(sq)

Вывод:

1
4
9
16
25

Как это работает

  1. __init__ сохраняет лимит n и инициализирует счётчик current = 0.
  2. __iter__ возвращает self — объект сам является своим итератором.
  3. __next__ увеличивает счётчик, проверяет лимит и возвращает квадрат. Когда элементы кончились — поднимает StopIteration.

Итератор-диапазон с шагом

Более практичный пример — итератор с заданным шагом:

class Range:
    """Аналог range(), но как явный класс-итератор."""

    def __init__(self, start, stop, step=1):
        self.current = start
        self.stop = stop
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.stop:
            raise StopIteration
        value = self.current
        self.current += self.step
        return value


print(list(Range(0, 10, 2)))  # чётные числа 0–8

Вывод:

[0, 2, 4, 6, 8]

Итератор исчерпывается

sq = Squares(3)

print(list(sq))   # [1, 4, 9]
print(list(sq))   # [] — итератор уже исчерпан!

# Чтобы пройти снова — нужен новый экземпляр
sq2 = Squares(3)
print(list(sq2))  # [1, 4, 9]

Вывод:

[1, 4, 9]
[]
[1, 4, 9]

Если вам нужно многократно перебирать коллекцию, сделайте объект итерируемым__iter__, возвращающим новый итератор), а не итератором. Тогда каждый for создаёт свежий итератор.

Встроенные итераторы

names = ["Алиса", "Боб", "Карл"]

# enumerate возвращает итератор пар (индекс, значение)
for i, name in enumerate(names, start=1):
    print(f"{i}. {name}")

print()

# zip объединяет несколько итерируемых
scores = [90, 85, 95]
for name, score in zip(names, scores):
    print(f"{name}: {score}")

Вывод:

1. Алиса
2. Боб
3. Карл

Алиса: 90
Боб: 85
Карл: 95

Коротко

  • Итератор реализует __iter__() и __next__() — это протокол итератора.
  • __next__() возвращает следующий элемент или поднимает StopIteration.
  • Итерируемое ≠ итератор: список порождает новый итератор каждый раз, итератор можно пройти лишь один раз.
  • Цикл for вызывает iter() и next() автоматически.
  • Встроенные итераторы: enumerate, zip, map, filter, reversed.
Проверьте себя
1. Что такое протокол итератора?
AНабор методов __getitem__ и __len__
BИнтерфейс с методами __iter__ и __next__
CВстроенная функция iter()
DЦикл for, применённый к коллекции
2. Итератор Square(5) был полностью перебран в цикле for. Что произойдёт при вызове next(square) после этого?
AИтератор «перемотается» и начнёт сначала
BВызовется исключение StopIteration
CВернётся последний элемент повторно
DВернётся None
3. Зачем метод __iter__ итератора возвращает self (сам объект)?
AЧтобы создавать копии итератора
BДля поддержки оператора in
CЧтобы итератор мог модифицировать сам себя
DЭто позволяет использовать итератор в цикле for и передавать в функции iter()

Закрепите практикой

Задачи с автоматической проверкой — решайте прямо здесь, не уходя из учебника.

py
Курс по теме
Пройдите курс «Python с нуля» — по шагам, с проверкой
8 уроков · ~14 ч · теория, упражнения и экзамен с бейджем
Открыть курс →