В чем разница между итератором и декоратором в Python?

«В чем разница между итератором и декоратором в Python?» — вопрос из категории Python, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Это совершенно разные концепции, решающие разные задачи.

Итератор (Iterator) — это объект, который позволяет последовательно обходить элементы коллекции (или потока данных). Он реализует протокол итерации через методы __iter__() (возвращает сам итератор) и __next__() (возвращает следующий элемент или вызывает StopIteration).

# Создание собственного итератора для генерации последовательности квадратов
class SquareIterator:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.limit:
            raise StopIteration
        result = self.current ** 2
        self.current += 1
        return result

# Использование
for num in SquareIterator(5):
    print(num)  # Вывод: 0, 1, 4, 9, 16

# Итераторы лежат в основе генераторов
squares_gen = (x**2 for x in range(5))  # generator expression

Декоратор (Decorator) — это функция, которая принимает другую функцию (или класс) и возвращает новую, модифицированную функцию, не изменяя исходный код. Это паттерн для добавления сквозной функциональности (логирование, кэширование, проверка прав).

import time
import functools

# Декоратор для замера времени выполнения и кэширования результата
def timer_and_cache(func):
    cache = {}

    @functools.wraps(func)  # Сохраняем метаданные оригинальной функции
    def wrapper(*args, **kwargs):
        # Ключ для кэша — аргументы функции
        cache_key = (args, tuple(kwargs.items()))

        if cache_key in cache:
            print(f"Возвращаем результат из кэша для {func.__name__}")
            return cache[cache_key]

        print(f"Запускаем {func.__name__}...")
        start_time = time.perf_counter()
        result = func(*args, **kwargs)  # Вызов оригинальной функции
        elapsed = time.perf_counter() - start_time

        cache[cache_key] = result
        print(f"{func.__name__} выполнилась за {elapsed:.4f} сек.")
        return result

    return wrapper

# Применение декоратора
@timer_and_cache
def expensive_computation(n):
    time.sleep(0.5)  # Имитация долгого вычисления
    return n * n

print(expensive_computation(5))  # Запускает вычисление
print(expensive_computation(5))  # Возвращает из кэша

Итог: Итератор — это про обход данных. Декоратор — про модификацию поведения функций.