История вопроса: Понятие итерируемости появилось в Python для унификации работы с коллекциями: списком, словарём, множеством и т.д. Любой объект, по которому можно пройтись с помощью цикла for, считается итерируемым. Это реализовано через определённые магические методы.
Проблема: Python требует определённых протоколов для корректной работы циклов и функций, связанных с последовательностями. Если пользователь неправильно реализует эти протоколы в своём классе, стандартные механизмы (for, list(), sum() и др.) не будут работать или будут вести себя неожиданно.
Решение:
Итерируемым является любой объект, реализующий метод __iter__. Итератор — тот, у кого есть метод __next__ и __iter__, возвращающий self. Обычно объект, возвращаемый __iter__, является итератором, но не обязательно. Пример:
class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3
Ключевые особенности:
__iter__.__next__, и __iter__ (возвращающий self).__iter__ эффективно реализуется, если требуется несколько независимых проходов по коллекции.Обязателен ли метод next для любого итерируемого объекта?
Нет. Для итерируемого объекта необходим только __iter__, возвращающий итератор. __next__ реализует только сам итератор. Например, у list нет метода __next__, но он итерируемый: его __iter__ возвращает экземпляр итератора.
lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False
Может ли объект быть итератором сам по себе?
Да, если реализует оба метода — и __iter__, и __next__.
Можно ли создать несколько итераторов с независимым состоянием для одной коллекции?
Да, если __iter__ возвращает каждый раз новый объект-итератор.
class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)
__next__ без __iter__ (или наоборот).Негативный кейс: Класс итератора хранит состояние (например, текущий индекс) на уровне класса, а не экземпляра, из-за чего параллельные обходы ломают друг друга. Плюсы:
Положительный кейс:
Каждый итератор хранит своё состояние в экземпляре, созданном в __iter__.
Плюсы: