Что такое декоратор в Python
Декоратор в Python — это функция, которая принимает другую функцию (или метод) в качестве аргумента и возвращает новую функцию, расширяющую или изменяющую поведение исходной, не изменяя её код. Декораторы применяются для логирования, замера времени выполнения, проверки прав доступа, кэширования и других сквозных задач.
Как это работает
Декоратор — это синтаксический сахар над присваиванием. Когда вы пишете:
@my_decorator
def func():
passэто эквивалентно:
def func():
pass
func = my_decorator(func)Декоратор обычно определяется как функция, которая принимает функцию и возвращает обёртку (wrapper). Внутри обёртки можно выполнять действия до и после вызова исходной функции.
Пример
Простейший декоратор для логирования вызовов:
import functools
def log_calls(func):
@functools.wraps(func) # сохраняет имя и docstring исходной функции
def wrapper(*args, **kwargs):
print(f"Вызов {func.__name__} с аргументами {args} {kwargs}")
result = func(*args, **kwargs)
print(f"Результат: {result}")
return result
return wrapper
@log_calls
def add(a, b):
"""Складывает два числа."""
return a + b
add(2, 3)
Вызов add с аргументами (2, 3) {}
Результат: 5
Здесь `log_calls` — декоратор, `wrapper` — обёртка, которая логирует вызов и результат. `functools.wraps` копирует метаданные исходной функции в обёртку, иначе `add.__name__` стал бы `'wrapper'`.
## Подводные камни
- **Потеря метаданных**: без `functools.wraps` имя и docstring функции заменяются на данные обёртки, что ломает интроспекцию и документацию.
- **Декораторы с аргументами**: если нужно передать параметры декоратору (например, уровень логирования), требуется дополнительный уровень вложенности:
```python
def repeat(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator- Порядок применения: декораторы применяются снизу вверх. Если написать
@aнад@b, то сначала применитсяb, затемa. - Декораторы для методов: при декорировании методов класса обёртка должна учитывать
self— он передаётся как первый аргумент, поэтому используйте*args, **kwargs.
Когда использовать
Декораторы оправданы, когда нужно:
- Логировать вызовы функций или их аргументы.
- Замерять время выполнения (например, для профилирования).
- Ограничивать доступ (проверка прав пользователя).
- Кэшировать результаты (например,
functools.lru_cache). - Реализовать паттерн "цепочка обязанностей" или "наблюдатель".
Избегайте излишнего использования: если логика не переиспользуется или усложняет чтение кода, лучше явно вызвать вспомогательную функцию внутри основной.
Коротко
- Декоратор — функция, принимающая функцию и возвращающая новую функцию, расширяющую поведение исходной.
- Синтаксис
@decorator— это присваиваниеfunc = decorator(func). - Используйте
functools.wraps, чтобы сохранить метаданные исходной функции. - Декораторы с аргументами требуют тройной вложенности: внешняя функция принимает аргументы, средняя — декорируемую функцию, внутренняя — обёртку.