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

Как итератор связан с for в Python?

1.7 Middle🔥 221 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Связь итератора и for в Python

Этот вопрос раскрывает глубокое понимание того, как работает цикл for под капотом. Это ключевой концепт Python, которая отделяет опытных разработчиков от новичков.

1. Протокол итератора в Python

Цикл for не магия — это просто синтаксический сахар, который опирается на два специальных метода:

  • __iter__() — возвращает объект итератора
  • __next__() — возвращает следующий элемент или вызывает StopIteration
# Что происходит под капотом:
# for item in collection:
#     print(item)

# Эквивалентно:
iterator = collection.__iter__()  # Получить итератор
while True:
    try:
        item = iterator.__next__()  # Получить следующий элемент
        print(item)
    except StopIteration:  # Когда элементов нет
        break

2. Простой пример: создаем свой итератор

class CountUp:
    """Итератор, который считает от 1 до max_value"""
    
    def __init__(self, max_value):
        self.max_value = max_value
        self.current = 0
    
    def __iter__(self):
        """Возвращает сам себя как итератор"""
        return self
    
    def __next__(self):
        """Возвращает следующий элемент или вызывает StopIteration"""
        self.current += 1
        if self.current > self.max_value:
            raise StopIteration  # Сигнал о конце последовательности
        return self.current

# Использование с for
counter = CountUp(3)
for num in counter:
    print(num)  # Выведет: 1, 2, 3

3. Итерируемое (Iterable) vs Итератор (Iterator)

Это два разных концепта, которых часто путают:

# Итерируемое (Iterable) — объект, у которого есть __iter__()
# Возвращает НОВЫЙ итератор при каждом вызове

class MyList:
    def __init__(self, items):
        self.items = items
    
    def __iter__(self):
        """Возвращает НОВЫЙ итератор"""
        return MyListIterator(self.items)

class MyListIterator:
    def __init__(self, items):
        self.items = items
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index >= len(self.items):
            raise StopIteration
        value = self.items[self.index]
        self.index += 1
        return value

# Использование
my_list = MyList([1, 2, 3])

# Каждый for создаёт новый итератор
for item in my_list:
    print(item)  # 1, 2, 3

for item in my_list:  # Новый итератор!
    print(item)  # 1, 2, 3 (снова)

# Напротив, сам итератор исчерпывается:
iterator = my_list.__iter__()
for item in iterator:
    print(item)  # 1, 2, 3

for item in iterator:  # Итератор уже исчерпан
    print(item)  # Ничего не выведет

4. Генераторы — синтаксический сахар

Генератор — это лёгкий способ создать итератор через yield:

# Способ 1: явный итератор (громоздко)
class CountUpIterator:
    def __init__(self, max_value):
        self.max_value = max_value
        self.current = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        self.current += 1
        if self.current > self.max_value:
            raise StopIteration
        return self.current

# Способ 2: генератор (элегантно)
def count_up(max_value):
    current = 0
    while current < max_value:
        current += 1
        yield current  # Переводит функцию в генератор

# Использование идентично
for num in count_up(3):
    print(num)  # 1, 2, 3

Под капотом генератор имеет __iter__() и __next__(), но вам не нужно их писать.

5. Итераторы в стандартной библиотеке

# iter() — встроенная функция для получения итератора
iterable = [1, 2, 3]
iterator = iter(iterable)

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3
# print(next(iterator))  # StopIteration

# itertools — мощная библиотека для работы с итераторами
from itertools import count, cycle, islice

# count() — бесконечный итератор
for num in islice(count(1), 3):  # islice ограничивает 3 элемента
    print(num)  # 1, 2, 3

# cycle() — циклический итератор
for item in islice(cycle([1, 2]), 5):
    print(item)  # 1, 2, 1, 2, 1

6. Ленивые вычисления (Lazy evaluation)

Это главное преимущество итераторов — они вычисляют значения по требованию:

# Список — вычисляет всё сразу (жадный — eager evaluation)
list_result = [x ** 2 for x in range(1000000)]
print(list_result[0])  # Заняло время на вычисление всех элементов

# Генератор — вычисляет по требованию (ленивый — lazy evaluation)
def squared():
    for x in range(1000000):
        yield x ** 2

generator = squared()
print(next(generator))  # Мгновенно, вычислен только первый элемент

# Практический пример: чтение большого файла
def read_large_file(filepath):
    """Читать файл строка за строкой, не загружая в память всё сразу"""
    with open(filepath) as f:
        for line in f:  # file object — это итератор
            yield line.strip()

# Использование
for line in read_large_file('huge_file.txt'):
    process_line(line)  # Обрабатываем по одной строке

7. Более сложный пример: Fibonacci

# Способ 1: через класс-итератор
class FibonacciIterator:
    def __init__(self, limit):
        self.limit = limit
        self.a, self.b = 0, 1
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.a > self.limit:
            raise StopIteration
        value = self.a
        self.a, self.b = self.b, self.a + self.b
        return value

for fib_num in FibonacciIterator(100):
    print(fib_num)  # 0, 1, 1, 2, 3, 5, 8, 13, ...

# Способ 2: через генератор (гораздо чище)
def fibonacci(limit):
    a, b = 0, 1
    while a <= limit:
        yield a
        a, b = b, a + b

for fib_num in fibonacci(100):
    print(fib_num)  # 0, 1, 1, 2, 3, 5, 8, 13, ...

8. Цепочки итераторов

from itertools import chain

list1 = [1, 2, 3]
list2 = [4, 5, 6]

# Объединить без создания нового списка
for item in chain(list1, list2):
    print(item)  # 1, 2, 3, 4, 5, 6

# Собственная реализация
def my_chain(*iterables):
    for iterable in iterables:
        for item in iterable:
            yield item

for item in my_chain(list1, list2):
    print(item)  # 1, 2, 3, 4, 5, 6

Ключевые моменты

  • for loop использует протокол итератора (__iter__() и __next__())
  • Итерируемое (Iterable) — есть __iter__(), возвращает новый итератор
  • Итератор (Iterator) — есть __next__(), исчерпывается после прохода
  • Генераторы — удобный способ создать итератор через yield
  • Ленивые вычисления — итераторы вычисляют значения по требованию
  • itertools — богатая библиотека для работы с итераторами
  • Используй генераторы для больших последовательностей и файлов

Понимание итераторов — это фундамент для написания эффективного Python кода, особенно при работе с большими объёмами данных.

Как итератор связан с for в Python? | PrepBro