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

Что такое __iter__?

3.0 Senior🔥 101 комментариев
#DevOps и инфраструктура#Django

Комментарии (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()