Что такое декоратор
Декоратор — это функция высшего порядка, которая принимает другую функцию в качестве аргумента, добавляет ей дополнительное поведение и возвращает новую функцию. Внутри декоратора используется вложенная функция-обёртка (wrapper), поэтому декораторы неразрывно связаны с замыканиями.
def simple_decorator(func):
def wrapper():
print("Before call")
func()
print("After call")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
Где применяются декораторы
- Логирование вызовов функций — запись аргументов и результатов в файл или консоль.
- Измерение времени выполнения — полезно при профилировании и оптимизации.
- Ограничение частоты вызовов (Throttling) — защита от спама, сетевые запросы.
- Проверка и валидация входных данных — API, формы, обработка ошибок.
- Автоматическое повторение выполнения (Retry) — повтор при сбоях сети или файловых операций.
- Ограничение доступа — аутентификация и авторизация.
- Кэширование результатов — ускорение повторных вычислений.
- Автоматическое изменение данных — форматирование текста, приведение регистра.
Декораторы для функций с аргументами
Декорируемые функции часто принимают аргументы. Чтобы декоратор работал с любыми функциями, обёртка должна принимать *args, **kwargs и передавать их дальше.
import logging
logging.basicConfig(
filename="functions.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
encoding="utf-8",
)
def log_decorator(func):
def wrapper(*args, **kwargs):
logging.info(f"Function {func.__name__} called with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
logging.info(f"Function {func.__name__} returned {result}")
return result
return wrapper
@log_decorator
def add(a: int, b: int) -> int:
return a + b
@log_decorator
def say_hello() -> None:
print("Привет!")
print(add(3, 5))
say_hello()
Возврат результата из вложенной функции
Если оригинальная функция возвращает значение, декоратор должен либо вернуть его, либо заменить на другой результат. Если из wrapper ничего не вернуть, результат будет потерян и вернётся None.
def upper_decorator(func):
def wrapper(*args, **kwargs):
print("Выполняем функцию и возвращаем результат")
return func(*args, **kwargs).upper()
return wrapper
@upper_decorator
def get_text() -> str:
return "hello"
result = get_text()
print("Результат:", result)
Декоратор functools.wraps
Без дополнительных мер декорированная функция теряет своё имя и документацию, потому что им заменяет внутренняя функция wrapper. functools.wraps(func) копирует метаданные оригинала в обёртку.
import functools
def simple_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("\nДекорированная функция")
return func(*args, **kwargs)
return wrapper
@simple_decorator
def example_function():
"""Это оригинальная функция."""
print("Привет!")
print("Имя:", example_function.__name__)
print("Документация:", example_function.__doc__)
example_function()
Декораторы с аргументами
Чтобы декоратор принимал собственные аргументы, создаётся функция-декоратор-фабрика, которая возвращает сам декоратор.
def message_decorator(message: str):
def decorator(func):
def wrapper(*args, **kwargs):
print(message)
return func(*args, **kwargs)
return wrapper
return decorator
@message_decorator("Начинаем выполнение")
def analyse_data() -> None:
print("Данные проанализированы")
analyse_data()
Использование нескольких декораторов
Декораторы можно комбинировать. Они применяются снизу вверх: сначала тот, что ближе к функции, затем следующий.
def border_decorator(func):
def wrapper(*args, **kwargs):
print("*" * 40)
func(*args, **kwargs)
print("*" * 40)
return wrapper
def repeat_decorator(times: int = 3):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@border_decorator
@repeat_decorator(3)
def print_line() -> None:
print("-" * 40)
print_line()