← Назад к вопросам

Какие магические методы нужно определить для создания собственного итератора?

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 — снова! Работает!

Вывод

Для создания собственного итератора нужны:

  1. __iter__() — возвращает объект итератора (обычно self)
  2. __next__() — возвращает следующий элемент или поднимает StopIteration

Правильный паттерн:

  • Iterable (контейнер) — определяет __iter__(), возвращающий новый Iterator
  • Iterator (проходчик) — определяет __iter__() (возвращает self) и __next__()

Это позволяет итерировать один контейнер несколько раз независимо.