Ответ
Декоратор в Python — это функция, которая принимает другую функцию и расширяет или изменяет её поведение, не модифицируя исходный код оборачиваемой функции. Это мощный инструмент для реализации аспектно-ориентированного программирования.
Основные применения:
- Логирование: автоматическое логирование вызовов функций и их аргументов.
- Кеширование (мемоизация): сохранение результатов дорогостоящих вызовов.
- Контроль доступа: проверка прав пользователя перед выполнением функции.
- Валидация: проверка входных данных или выходных результатов.
- Замер времени выполнения: профилирование кода.
- Повторные попытки (retry): автоматический повтор вызова при сбоях.
Пример декоратора для замера времени:
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"Функция {func.__name__!r} выполнилась за {end_time - start_time:.4f} сек")
return result
return wrapper
@timer
def calculate_sum(n):
return sum(range(n))
# При вызове будет выведено время выполнения
result = calculate_sum(1_000_000)
Декораторы помогают соблюдать принцип единственной ответственности (Single Responsibility Principle) и избегать дублирования кода.
Ответ 18+ 🔞
Давай разберёмся с этими декораторами, а то народ часто нихуя не понимает, что это за зверь такой.
Представь себе, что у тебя есть функция — ну, обычная, делает свои дела. А теперь ты хочешь, чтобы она перед работой логировала, кто её вызвал, или замеряла время, или проверяла права. Так вот, декоратор — это как раз та хитрая жопа, которая оборачивает твою функцию, не лезя внутрь её кода. Ничего не меняя, просто добавляет сверху какую-то логику. Красота, ёпта!
Где это реально пригождается, спросишь? Да овердохуища мест:
- Логирование: чтобы автоматом писать в лог, кто, когда и с какими аргументами дернул функцию. Удобно, когда потом ищешь, где всё пошло по пизде.
- Кеширование (или мемоизация): если функция, например, факториал считает, и ты её десять раз с одним аргументом вызываешь, зачем каждый раз пересчитывать? Декоратор запомнит результат и отдаст из кеша. Экономия, блядь, ресурсов — просто песня.
- Контроль доступа: перед тем как дать доступ к админке, проверить, а не полупидор ли ты случайно. Если нет прав — нахуй иди.
- Валидация данных: проверить, что на входе не хуй в пальто, а нормальные аргументы.
- Замер времени выполнения: чтобы понять, какая функция жрёт процессорное время как не в себя.
- Повторные попытки (retry): если функция упала из-за временной ошибки сети, декоратор может сам её перезапустить пару раз, прежде чем сдаться.
Вот тебе живой пример — декоратор, который засекает время работы функции:
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"Функция {func.__name__!r} выполнилась за {end_time - start_time:.4f} сек")
return result
return wrapper
@timer
def calculate_sum(n):
return sum(range(n))
# При вызове будет выведено время выполнения
result = calculate_sum(1_000_000)
Смотри, что тут происходит: мы написали функцию timer, которая принимает другую функцию (func). Внутри неё создаётся обёртка (wrapper), которая:
- Запоминает время старта.
- Вызывает оригинальную функцию.
- Запоминает время окончания.
- Печатает разницу.
- Возвращает результат оригинальной функции.
А эта магия @timer над calculate_sum — это просто красивый синтаксический сахар. Всё равно что написать calculate_sum = timer(calculate_sum). Функция calculate_sum теперь на самом деле — это wrapper, который и время засекает, и оригинальную функцию вызывает.
Зачем @functools.wraps(func)? А чтобы не потерять имя и документацию оригинальной функции. Без него после декорирования calculate_sum.__name__ был бы 'wrapper', и это, блядь, сбивало бы с толку при отладке.
В общем, суть в чём: декораторы помогают не превращать код в спагетти, где в каждой функции вручную пишешь логирование, замеры и проверки. Выносишь общую логику в декоратор — и применяешь её где надо одной строчкой. Принцип единственной ответственности наше всё, дублирования кода — ноль. Удобная, блядь, штука, если разобраться.