> Что такое декоратор в Python и для чего он используется? (Python)

Уровень: junior · Роль: qa · Категория: Технические вопросы

Компании: Домклик, Сбер

Стек: Python

> Пример ответа

Короткий ответ

Декоратор в Python - это функция, которая принимает другую функцию (или класс) и возвращает новую функцию (или класс), расширяя её поведение без изменения исходного кода. Используется для добавления логирования, проверки прав, кэширования, замеров времени и других сквозных задач. Синтаксически применяется через @decorator перед определением функции. Это мощный инструмент метапрограммирования, основанный на замыканиях и функциях высшего порядка.

Подробное объяснение

Декоратор - это синтаксический сахар над обычным присваиванием: @decorator эквивалентно func = decorator(func). Функция-декоратор принимает целевую функцию как аргумент и возвращает обёртку (wrapper), которая обычно принимает *args, **kwargs, чтобы передать их исходной функции.

Основные варианты использования:

  • логирование вызовов и аргументов
  • контроль доступа (например, проверка роли пользователя)
  • кэширование результатов (например, @lru_cache)
  • измерение времени выполнения
  • валидация входных данных
  • ретраи (повторные попытки) при ошибках

Декораторы могут быть параметризованными (с аргументами) - тогда внешняя функция возвращает декоратор. Также существуют декораторы классов (например, @dataclass), которые модифицируют класс целиком.

Важно: декоратор применяется один раз при определении функции, а не при каждом вызове. Обёртка выполняется при каждом вызове, но сам декоратор - только в момент определения.

На практике

Для QA-инженера декораторы полезны при написании автотестов и вспомогательных утилит. Например, можно создать декоратор для повторного запуска flaky-тестов, для логирования шагов теста, для проверки предусловий или для сохранения скриншотов при падении.

Также декораторы часто встречаются в тестовых фреймворках (pytest, allure) - например, @pytest.mark.parametrize, @allure.step. Понимание их устройства помогает читать чужой код и отлаживать проблемы, связанные с обёртками (например, потеря имени функции без functools.wraps).

Пример кода

PYTHON
import functools
import time
def retry(max_attempts=3, delay=1):
"""Декоратор для повторных попыток выполнения функции."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=2, delay=0.5)
def unstable_request():
# имитация нестабильного API
import random
if random.random() < 0.5:
raise ConnectionError("Network error")
return "OK"
print(unstable_request())

Как отвечать на собеседовании

Начни с простого определения: "декоратор - это функция, которая оборачивает другую функцию". Затем покажи синтаксис и объясни, что @decorator - это просто func = decorator(func). Приведи один-два реальных примера из своей практики (например, логирование или ретраи). Если спросят про параметризованные декораторы - объясни трёхуровневую вложенность. Упомяни functools.wraps для сохранения метаданных. Не углубляйся в сложные темы вроде декораторов классов, если не спрашивают.

Что проверяет интервьюер

Интервьюер проверяет:

  • понимание принципа функций высшего порядка и замыканий
  • знание синтаксиса @ и эквивалентного присваивания
  • умение объяснить, когда декораторы полезны, а когда вредны (избыточность)
  • знание functools.wraps и проблем с метаданными
  • способность написать простой декоратор с нуля без подсказок

Для junior-уровня достаточно уверенного объяснения базового механизма и одного примера. Если кандидат путается в порядке аргументов параметризованного декоратора - это нормально, но базовую обёртку должен объяснить чётко.

Типичные ошибки

  • Забывают functools.wraps - теряется __name__ и __doc__ функции, что ломает интроспекцию и документацию.
  • Путают порядок вложенности для параметризованных декораторов: сначала аргументы декоратора, потом функция, потом *args, **kwargs.
  • Считают, что декоратор выполняется при каждом вызове функции - на самом деле он выполняется один раз при определении.
  • Не передают *args, **kwargs в обёртке - функция с аргументами перестаёт работать.
  • Используют декораторы там, где достаточно обычного вызова функции - усложняют код без необходимости.
  • Возвращают результат без return в обёртке - функция возвращает None.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью