Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
iter: Протокол итерирования в Python
__iter__ — это магический (dunder) метод, который делает объект итерируемым (iterable). Он возвращает итератор — объект, который знает, как проходить по элементам последовательности.
Итерируемость vs Итератор
Разница критична:
# Итерируемый объект (iterable)
# Имеет метод __iter__, который возвращает итератор
list_obj = [1, 2, 3]
for item in list_obj: # list_obj итерируемый
print(item)
# Итератор (iterator)
# Имеет __iter__ и __next__, знает текущую позицию
iterator = iter(list_obj) # __iter__ вызывается автоматически
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# next(iterator) # StopIteration exception
Протокол итерирования в Python:
┌─────────────────────┐
│ Iterable (объект) │
│ (имеет __iter__) │
└──────────┬──────────┘
│
iter() вызывает
__iter__()
│
↓
┌─────────────────────┐
│ Iterator (объект) │
│ (имеет __next__) │
└──────────┬──────────┘
│
next() вызывает
__next__()
│
↓
Возвращает значение
или StopIteration
Базовый пример
class Counter:
def __init__(self, max_value: int):
self.max_value = max_value
self.current = 0
def __iter__(self):
"""Возвращает итератор."""
self.current = 0 # Сбрасываем счётчик
return self
def __next__(self):
"""Возвращает следующий элемент."""
if self.current < self.max_value:
self.current += 1
return self.current
else:
raise StopIteration # Конец итерации
# Использование:
counter = Counter(3)
for num in counter:
print(num) # 1, 2, 3
# Это работает потому что:
# 1. for автоматически вызывает iter(counter) -> __iter__()
# 2. __iter__() возвращает self
# 3. for вызывает next(iterator) -> __next__()
# 4. __next__() возвращает значения или StopIteration
Разница: возвращение self vs новый объект
Вариант 1: Возвращаем self (итератор это сам объект)
class SimpleCounter:
def __init__(self, max_value: int):
self.max_value = max_value
self.current = 0
def __iter__(self):
return self # Итератор это сам объект
def __next__(self):
if self.current < self.max_value:
self.current += 1
return self.current
raise StopIteration
counter = SimpleCounter(3)
for num in counter:
print(num) # 1, 2, 3
# Проблема: если пытаться итерировать дважды
for num in counter: # Второй раз: ничего не напечатается!
print(num) # counter.current уже = 3
Вариант 2: Возвращаем новый итератор (рекомендуется)
class BetterCounter:
def __init__(self, max_value: int):
self.max_value = max_value
def __iter__(self):
return CounterIterator(self.max_value)
class CounterIterator:
def __init__(self, max_value: int):
self.max_value = max_value
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.max_value:
self.current += 1
return self.current
raise StopIteration
counter = BetterCounter(3)
for num in counter:
print(num) # 1, 2, 3
for num in counter: # Второй раз: снова 1, 2, 3
print(num) # Работает!
Практические примеры
1. Итерируемая очередь (Queue)
class Queue:
def __init__(self, items: list):
self.items = items.copy()
def __iter__(self):
return QueueIterator(self.items)
class QueueIterator:
def __init__(self, items: list):
self.items = items.copy()
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.items):
result = self.items[self.index]
self.index += 1
return result
raise StopIteration
queue = Queue([10, 20, 30])
for item in queue:
print(item) # 10, 20, 30
2. Бесконечный итератор
class InfiniteCounter:
def __init__(self, start: int = 0):
self.current = start
def __iter__(self):
return self
def __next__(self):
result = self.current
self.current += 1
return result
# Использование с ограничением
for num in InfiniteCounter(100):
if num >= 105:
break
print(num) # 100, 101, 102, 103, 104
3. Итератор с фильтром
class FilteredIterator:
def __init__(self, data: list, condition):
self.data = data
self.condition = condition
self.index = 0
def __iter__(self):
return self
def __next__(self):
while self.index < len(self.data):
item = self.data[self.index]
self.index += 1
if self.condition(item):
return item
raise StopIteration
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
filtered = FilteredIterator(data, lambda x: x % 2 == 0)
for num in filtered:
print(num) # 2, 4, 6, 8
Встроенные функции с iter
# iter() вызывает __iter__
my_list = [1, 2, 3]
iterator = iter(my_list) # вызывает my_list.__iter__()
# next() вызывает __next__
print(next(iterator)) # 1
# list() потребляет итератор
my_list = [1, 2, 3]
iterator = iter(my_list)
result = list(iterator) # [1, 2, 3]
# zip(), map(), filter() работают с итераторами
data1 = [1, 2, 3]
data2 = ['a', 'b', 'c']
for num, letter in zip(data1, data2):
print(num, letter) # Использует __iter__ обоих списков
Проверка: является ли объект итерируемым
from collections.abc import Iterable, Iterator
# Проверка на итерируемость
my_list = [1, 2, 3]
print(isinstance(my_list, Iterable)) # True
my_int = 42
print(isinstance(my_int, Iterable)) # False
# Проверка на итератор
iterator = iter(my_list)
print(isinstance(iterator, Iterator)) # True
print(isinstance(my_list, Iterator)) # False (это iterable, но не iterator)
yield и генераторы (более простой способ)
# Вместо написания класса с __iter__ и __next__
# можно использовать yield
def counter_generator(max_value: int):
current = 0
while current < max_value:
current += 1
yield current
# Генератор это итератор!
for num in counter_generator(3):
print(num) # 1, 2, 3
# Это эквивалентно Counter классу выше
Производительность
# Генератор (память эффективен)
def gen_numbers(n):
for i in range(n):
yield i ** 2
# Список (весь в памяти)
list_numbers = [i ** 2 for i in range(10**6)]
# Генератор берёт значения по требованию
for num in gen_numbers(10**6):
if num > 1000000:
break
Практический пример: чтение файла по строкам
class FileReader:
def __init__(self, filename: str):
self.filename = filename
def __iter__(self):
return FileReaderIterator(self.filename)
class FileReaderIterator:
def __init__(self, filename: str):
self.file = open(filename, 'r')
def __iter__(self):
return self
def __next__(self):
line = self.file.readline()
if line:
return line.rstrip('\n')
else:
self.file.close()
raise StopIteration
# Использование:
for line in FileReader('data.txt'):
print(line)
Ключевые моменты
__iter__возвращает итератор__next__возвращает следующее значение илиStopIteration- Итерируемый объект != Итератор
- Генераторы (yield) проще, чем классы с
__iter__и__next__ - for цикл это синтаксический сахар над
iter()иnext()