Цикл for-in в Python реализуется через протокол итерации, основанный на двух специальных методах: __iter__() и __next__(). Когда цикл начинается, Python вызывает iter(obj) (ищет метод __iter__). Если возвращается объект-итератор, в котором определён __next__(), цикл начинает вызывать этот метод для получения каждого следующего элемента.
Если элемент выдан успешно — итерация продолжается. Когда выбрасывается исключение StopIteration, цикл завершает работу.
Пример собственного итератора:
class Counter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1 for num in Counter(1, 3): print(num) # Выведет 1, 2, 3
Тонкость: Многие стандартные структуры Python реализуют протокол итератора; вы легко можете сделать свои объекты такими же.
Можно ли использовать цикл for-in с любым объектом Python? Чем итерируемый объект отличается от итератора?
Ответ: Нет, цикл for-in требует, чтобы объект был итерируемым, т.е. реализовывал метод __iter__(), который возвращает итератор (объект с методом __next__()). Сам итератор возвращает себя через __iter__(), а итерируемый объект — нет.
Пример:
lst = [1, 2, 3] iterator = iter(lst) print(hasattr(lst, '__iter__')) # True print(hasattr(iterator, '__next__')) # True (итератор) print(hasattr(lst, '__next__')) # False (не итератор)
История
Проект: Обработка больших файлов.
Проблема: В проекте пытались повторить проход по файловому объекту (итерацию по строкам файла) дважды без явного открытия нового файла — и на второй итерации не получили ни одной строки (указатель уже в конце файла!).
История
Проект: Интеграция своего класса коллекции с фреймворком.
Проблема: Класс реализовал только
__getitem__, но не__iter__, и не был совместим с генераторами и стандартными циклами (for). Из-за этого ломались сторонние библиотеки, ожидавшие итератор.
История
Проект: Использование map/filter на кастомных коллекциях.
Проблема: Собственный класс списка не реализовал
__iter__и__next__, и оказался не совместим со многими стандартными функциями Python, что выявилось только после интеграционных тестов и потребовало значительной переработки.