Итераторы в Python: iter, next и протокол итерации простыми словами
Если вы когда‑нибудь использовали цикл for в Python, вы уже пользовались итераторами. В этой статье мы разберёмся, что такое итерабельные объекты и итераторы, как устроен протокол итерации, зачем нужны iter() и next(), и как писать собственные итераторы. Материал ориентирован на начинающих и продолжающих разработчиков.
Итерабельный vs итератор: в чём разница?
Итерабельный объект (iterable) — это то, по чему можно пройтись в цикле for (списки, строки, словари, файлы и т.д.). У него есть метод __iter__(), который возвращает итератор.
Итератор (iterator) — это объект с методом __next__(), который выдаёт элементы по одному и поднимает StopIteration, когда элементы закончились.
s = "python"
it = iter(s) # получаем итератор из строки
print(next(it)) # 'p'
print(next(it)) # 'y'
print(list(it)) # оставшиеся символы ['t', 'h', 'o', 'n']
# print(next(it)) # StopIteration: элементы закончились
Как цикл for работает под капотом
Цикл for сам вызывает iter(obj), а затем многократно вызывает next(it) до StopIteration. Эквивалент на низком уровне выглядит так:
def for_like(iterable):
it = iter(iterable)
while True:
try:
item = next(it)
except StopIteration:
break
print(item)
for_like([1, 2, 3])
Практика: свой класс-итератор
Напишем итератор обратного отсчёта. Он сам является итератором (возвращает себя в __iter__) и выдаёт значения в __next__.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self # сам итератор
def __next__(self):
if self.current < 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for n in Countdown(3):
print(n)
# 3, 2, 1, 0
Важно: этот итератор исчерпаемый. После одного прохода он пуст. Чтобы начать заново, создайте новый объект.
Частая ошибка: исчерпание итератора
it = iter([10, 20, 30])
print(list(it)) # [10, 20, 30]
print(list(it)) # [] — уже пусто!
Решение — получать новый итератор из исходного итерируемого или аккуратно планировать, сколько раз вы проходите по итератору. Для дублирования потока можно использовать itertools.tee, но помните о накладных расходах по памяти.
iter(callable, sentinel): элегантный цикл до «стоп‑значения»
iter умеет создавать итератор из вызываемого объекта и «сторожевого» значения. Это удобно для чтения построчно до пустой строки или чтения блоками.
# Читаем файл построчно до пустой строки без while True
with open("data.txt", "r", encoding="utf-8") as f:
for line in iter(f.readline, ""):
print(line.rstrip())
Ещё пример — счётчик случайных чисел до тех пор, пока не выпадет 0:
import random
for x in iter(lambda: random.randint(0, 5), 0):
print(x)
# цикл завершится, когда лямбда вернёт 0
itertools: мощные строительные блоки итераций
Модуль itertools даёт «ленивые» инструменты для быстрой обработки потоков данных без лишней памяти.
from itertools import islice, chain, count, takewhile
# 1) Взять первые 5 строк большого файла — без чтения целиком
with open("big.log", "r", encoding="utf-8") as f:
head5 = list(islice(f, 5))
# 2) Склеить несколько источников в один поток
combined = chain([1, 2], (x for x in [3, 4]), {5, 6})
print(list(combined)) # [1, 2, 3, 4, 5, 6] (порядок множеств не гарантируется)
# 3) Генерировать числа, пока условие истинно
naturals = count(1)
first_under_50 = list(takewhile(lambda n: n < 50, naturals))
Помощники итераций: enumerate, zip и распаковка
enumerate добавляет индекс к элементам, а zip «склеивает» несколько итерируемых по позициям.
langs = ["Python", "Go", "Rust"]
for i, name in enumerate(langs, start=1):
print(i, name)
scores = [95, 87, 90]
for lang, score in zip(langs, scores):
print(f"{lang}: {score}")
Со словарями итерируйтесь по .items() для пар ключ‑значение:
conf = {"host": "localhost", "port": 5432}
for key, value in conf.items():
print(key, value)
Типы и аннотации: Iterable и Iterator
Для типизации используйте collections.abc.Iterable и collections.abc.Iterator — это помогает документировать контракт функции и ловить ошибки раньше.
from collections.abc import Iterable, Iterator
from typing import Any
def first(iterable: Iterable[Any]) -> Any:
return next(iter(iterable))
def take(n: int, it: Iterator[Any]) -> list[Any]:
return [next(it) for _ in range(n)]
Когда писать итератор-класс, а когда — генератор?
- Если состояние сложное и нужно чётко контролировать ресурс — подойдёт класс с
__iter__и__next__. - Если логика последовательна и линейна — проще использовать генератор-функцию с
yield(он тоже создаёт итератор).
def countdown(n):
while n >= 0:
yield n
n -= 1
print(list(countdown(3))) # [3, 2, 1, 0]
Типовые ошибки и советы
- TypeError: 'int' object is not iterable. Проверяйте, что передаёте в
forи функции, ожидающие итерируемые. - Исчерпание итератора. Планируйте однократный проход или сохраняйте данные, если нужно несколько проходов. Для повторного обхода берите новый итератор:
iter(iterable). - Не модифицируйте коллекцию во время итерации. Вместо этого создайте новую коллекцию или итерируйтесь по копии.
- Избегайте лишних list() над большими потоками. Работайте «лениво» (через
itertools), преобразуйте в список только когда нужно.
Чек‑лист перед продакшеном
- Вы уверены, что итератор используется один раз? Если нет — создавайте новый или примените
tee. - Сигнатуры аннотированы
IterableилиIterator? - Есть ли «узкие места» памяти из‑за
list()илиsorted()над огромными потоками? - Можно ли заменить «ручной» цикл на комбинацию
map/filter/itertoolsдля читаемости и ленивости?
Что дальше изучать
Освоив протокол итерации, вы легче разберётесь с генераторами, потоковой обработкой данных и построением собственных API. Хотите системно прокачать Python с практикой и обратной связью? Рекомендую пройти пошаговый курс «Программирование на Python с Нуля до Гуру» — начните с бесплатного урока и двигайтесь к трудоустройству.
Вывод
Итераторы — фундамент Python. Понимание iter(), next(), StopIteration и ленивых инструментов позволяет писать быстрый, читаемый и экономный по памяти код. Применяйте показанные приёмы в проектах — и ваш код станет надёжнее и эффективнее.
-
Создано 05.01.2026 17:01:14
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.