← Назад к вопросам
Какие магические методы нужно определить для создания собственного итератора?
2.0 Middle🔥 251 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Магические методы для создания собственного итератора
Для создания собственного итератора в Python нужно определить два магических метода: __iter__() и __next__().
Два ключевых метода
1. __iter__() — возвращает итератор
def __iter__(self):
return self # Обычно возвращает сам объект
2. __next__() — возвращает следующий элемент
def __next__(self):
if элемент_есть:
return элемент
else:
raise StopIteration # Сигнал окончания
Простейший пример: подсчёт от 1 до N
class CountUp:
"""Итератор, который считает от 1 до max"""
def __init__(self, max):
self.max = max
self.current = 0
def __iter__(self):
# __iter__() должен вернуть объект итератора
# Обычно это сам объект (self)
return self
def __next__(self):
# __next__() должен вернуть следующий элемент
# или поднять StopIteration, когда элементы закончились
self.current += 1
if self.current <= self.max:
return self.current
else:
raise StopIteration
# Использование
counter = CountUp(3)
for num in counter:
print(num) # 1, 2, 3
# Эквивалент без for:
counter = CountUp(3)
while True:
try:
print(next(counter)) # Вызывает __next__()
except StopIteration:
break # Выход когда StopIteration поднята
Разница между Iterable и Iterator
Iterable (Итерируемый объект) — объект, который имеет __iter__():
class MyIterable:
def __iter__(self):
# Должен вернуть Iterator
return MyIterator()
Iterator (Итератор) — объект, который имеет __iter__() И __next__():
class MyIterator:
def __init__(self):
self.current = 0
def __iter__(self):
return self # Iterator возвращает себя
def __next__(self):
self.current += 1
if self.current <= 5:
return self.current
raise StopIteration
Правильный паттерн: Iterable и Iterator отдельно
# ПРАВИЛЬНО: Iterable создаёт новый Iterator
class RangeIterable:
def __init__(self, max):
self.max = max
def __iter__(self):
return RangeIterator(self.max) # Создаём новый итератор
class RangeIterator:
def __init__(self, max):
self.max = max
self.current = 0
def __iter__(self):
return self
def __next__(self):
self.current += 1
if self.current <= self.max:
return self.current
raise StopIteration
# Использование: можем итерировать несколько раз
rng = RangeIterable(3)
for num in rng: # Первая итерация
print(num) # 1, 2, 3
for num in rng: # Вторая итерация (работает!)
print(num) # 1, 2, 3 (снова)
Пример: Итератор по файлам в папке
import os
class FileIterator:
"""Итератор по файлам в директории"""
def __init__(self, directory):
self.directory = directory
self.files = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.files):
file = self.files[self.index]
self.index += 1
return file
else:
raise StopIteration
# Использование
iter_files = FileIterator("/home/user/documents")
for filename in iter_files:
print(filename)
Пример: Бесконечный итератор
class InfiniteCounter:
"""Итератор, который считает бесконечно"""
def __init__(self, start=0):
self.current = start
def __iter__(self):
return self
def __next__(self):
result = self.current
self.current += 1
return result
# Использование
counter = InfiniteCounter()
for i, num in enumerate(counter):
print(num) # 0, 1, 2, 3, ...
if i >= 4: # Останавливаем после 5 элементов
break
Пример: Итератор с состоянием (фибоначчи)
class FibonacciIterator:
"""Итератор, генерирующий последовательность Фибоначчи"""
def __init__(self, max_count=10):
self.max_count = max_count
self.count = 0
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
result = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return result
# Использование
fib = FibonacciIterator(8)
for num in fib:
print(num) # 0, 1, 1, 2, 3, 5, 8, 13
Пример: Итератор с фильтром
class FilterIterator:
"""Итератор, который фильтрует элементы"""
def __init__(self, iterable, predicate):
self.iterable = iter(iterable) # Получаем итератор из iterable
self.predicate = predicate
def __iter__(self):
return self
def __next__(self):
while True:
item = next(self.iterable) # Получаем следующий элемент
if self.predicate(item):
return item
# Иначе продолжаем цикл (пропускаем элемент)
# Использование: фильтруем чётные числа
filter_iter = FilterIterator([1, 2, 3, 4, 5, 6], lambda x: x % 2 == 0)
for num in filter_iter:
print(num) # 2, 4, 6
Пример: Итератор, оборачивающий список
class ReversedListIterator:
"""Итератор по списку в обратном порядке"""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
# Использование
reversal = ReversedListIterator(["apple", "banana", "cherry"])
for item in reversal:
print(item) # cherry, banana, apple
Обработка исключений в __next__()
class SafeIterator:
"""Итератор, который обрабатывает исключения"""
def __init__(self, items):
self.items = items
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
if self.index >= len(self.items):
raise StopIteration
item = self.items[self.index]
self.index += 1
return item
except IndexError:
raise StopIteration # Преобразуем IndexError в StopIteration
# Использование
safe_iter = SafeIterator([10, 20, 30])
for num in safe_iter:
print(num) # 10, 20, 30
Разница: для чего __iter__()?
class MyIterator:
def __init__(self):
self.count = 0
def __iter__(self):
# Если __iter__() вернёт новый итератор:
# new_iter = iter(my_iterator) # Всё ОК
# Если __iter__() вернёт самого себя:
return self # Стандартный паттерн
def __next__(self):
self.count += 1
if self.count <= 3:
return self.count
raise StopIteration
# Проблема: если использовать один итератор дважды
it = MyIterator()
print(list(it)) # [1, 2, 3]
print(list(it)) # [] — пусто! Итератор исчерпан
Лучшая практика: Iterable как контейнер
class CounterIterable:
"""Контейнер (Iterable), а не итератор"""
def __init__(self, n):
self.n = n
def __iter__(self):
return CounterIterator(self.n)
class CounterIterator:
"""Фактический итератор"""
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:
return self.current
raise StopIteration
# Идеально: можем итерировать несколько раз
counter = CounterIterable(3)
for i in counter:
print(i) # 1, 2, 3
for i in counter:
print(i) # 1, 2, 3 — снова! Работает!
Вывод
Для создания собственного итератора нужны:
__iter__()— возвращает объект итератора (обычноself)__next__()— возвращает следующий элемент или поднимаетStopIteration
Правильный паттерн:
- Iterable (контейнер) — определяет
__iter__(), возвращающий новый Iterator - Iterator (проходчик) — определяет
__iter__()(возвращаетself) и__next__()
Это позволяет итерировать один контейнер несколько раз независимо.