Декораторы — это функции, которые принимают другую функцию и возвращают новую функцию с расширенным или видоизменённым поведением. Они часто используются для логирования, проверки прав, кеширования, времени выполнения и прочего.
Декораторы реализуются с помощью замыканий (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 разработчик реализовал декоратор с параметрами, но забыл правильно вложить функции (была двухуровневая вложенность вместо трёх). В результате декоратор не мог принимать параметры.