ПрограммированиеBackend разработчик

Объясните, как работают декораторы в Python. В чем их основная польза, как они реализуются, и какие тонкости могут возникнуть при их применении?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Декораторы — это функции, которые принимают другую функцию и возвращают новую функцию с расширенным или видоизменённым поведением. Они часто используются для логирования, проверки прав, кеширования, времени выполнения и прочего.

Декораторы реализуются с помощью замыканий (closure) или классов, имплементирующих метод __call__. Классический синтаксис:

# Простой декоратор def simple_decorator(func): def wrapper(*args, **kwargs): print("До вызова функции") result = func(*args, **kwargs) print("После вызова функции") return result return wrapper @simple_decorator def my_func(): print("Основная функция") my_func()

В результате исполнится так:

До вызова функции
Основная функция
После вызова функции

Основная польза — инкапсуляция повторяющейся логики вне основной бизнес-логики.

Тонкости:

  • Без использования functools.wraps теряется имя исходной функции и её docstring.
  • Если декоратор принимает аргументы, то пишется тройная вложенность функций.

Вопрос с подвохом

Вопрос: "Если я задекорирую функцию декоратором, а потом попытаюсь получить имя функции через __name__, что я увижу и как сохранить оригинальное имя?"

Ответ:

По умолчанию имя изменится на имя обёртки (обычно wrapper). Чтобы сохранить оригинальные метаданные, используйте functools.wraps:

import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # выведет 'foo', а не 'wrapper'

Примеры реальных ошибок из-за незнания тонкостей темы


История

В крупной автоматизации ресурсов после обёртывания функций декораторами сломалась система автотестов, использующая рефлексию по именам тестовых функций. Проблема была в отсутствии functools.wraps.


История

Декоратор, добавляющий логирование, не поддерживал функции с разными сигнатурами, т.к. не использовались *args, **kwargs. Часть функций просто ломалась Silent Fail.


История

В проекте с авторизацией в REST API разработчик реализовал декоратор с параметрами, но забыл правильно вложить функции (была двухуровневая вложенность вместо трёх). В результате декоратор не мог принимать параметры.