Ответ
Декоратор в Python — это функция, которая принимает другую функцию (или класс) в качестве аргумента и возвращает новую, модифицированную функцию, обычно расширяя или изменяя её поведение. Это реализация паттерна проектирования «Декоратор», позволяющая добавлять новую функциональность к существующим объектам динамически и без изменения их исходного кода.
Зачем это нужно? Для сквозной функциональности (cross-cutting concerns), которую нужно применять ко многим функциям: логирование, тайминг, кэширование, проверка прав доступа, валидация аргументов.
Базовый пример создания и использования:
def timer_decorator(func):
"""Декоратор, замеряющий время выполнения функции."""
import time
def wrapper(*args, **kwargs): # Принимает любые аргументы
start_time = time.perf_counter()
result = func(*args, **kwargs) # Вызов оригинальной функции
end_time = time.perf_counter()
print(f"Функция {func.__name__} выполнилась за {end_time - start_time:.4f} сек.")
return result
return wrapper
# Применение декоратора с помощью синтаксиса @
@timer_decorator
def calculate_sum(n):
"""Вычисляет сумму чисел от 1 до n."""
return sum(range(1, n+1))
# Вызов выглядит как обычно, но работает с доп. функциональностью
print(calculate_sum(1_000_000))
# Вывод:
# Функция calculate_sum выполнилась за 0.0453 сек.
# 500000500000
Встроенные и популярные декораторы:
@staticmethod,@classmethod,@property— для методов классов.@functools.lru_cache(maxsize=128)— для мемоизации (кэширования) результатов функции.@dataclasses.dataclass(Python 3.7+) — для автоматической генерации boilerplate-кода в классах.
Ответ 18+ 🔞
Вот тебе, дружище, история про декораторы в Python. Сидишь ты такой, пишешь код, и тут бац — понимаешь, что полсотни функций надо обернуть в одинаковую логику: засечь время, залогировать, проверить права. И тут тебя осеняет: «Ёпта, я же не буду это в каждую функцию копипастить, я ж не маньяк!» И вот тут на сцену выходит декоратор — хитрая жопа, которая позволяет навесить на любую функцию дополнительное поведение, как будто ты ей плащ-невидимку подарил, только наоборот.
Представь, что твоя функция — это обычный бутерброд. А декоратор — это волшебный аппарат, который этот бутерброд берёт, заворачивает в хрустящий бекон (логирование), посыпает сыром (тайминг) и ещё сверху соусом поливает (кеширование). И всё это — не ковыряясь вилкой в самой колбасе. Красота, правда?
Как эта магия устроена под капотом? По сути, декоратор — это просто функция, которая принимает другую функцию, делает с ней что-то и возвращает новую, улучшенную. Всё, блядь, гениальное просто.
Смотри на примере, который сам от себя охуевает от простоты. Допустим, мы хотим замерять, сколько выполняется любая функция.
def timer_decorator(func): # Вот наш декоратор. Принимает функцию (func) как аргумент.
"""Декоратор, замеряющий время выполнения функции."""
import time
def wrapper(*args, **kwargs): # А это обёртка. Она принимает ВСЕ аргументы, которые могли передать оригинальной функции.
start_time = time.perf_counter()
result = func(*args, **kwargs) # Тут мы, наконец, вызываем оригинальную функцию с её родными аргументами.
end_time = time.perf_counter()
print(f"Функция {func.__name__} выполнилась за {end_time - start_time:.4f} сек.")
return result # И возвращаем её результат, как будто так и было.
return wrapper # Декоратор возвращает нашу обёрнутую функцию.
# Применяем эту красоту с помощью собачки @
@timer_decorator
def calculate_sum(n):
"""Вычисляет сумму чисел от 1 до n."""
return sum(range(1, n+1))
# Вызываем как обычно, а под капотом — волшебство
print(calculate_sum(1_000_000))
# На экране будет:
# Функция calculate_sum выполнилась за 0.0453 сек.
# 500000500000
Видишь? Мы просто написали @timer_decorator над функцией, и теперь при каждом её вызове автоматически срабатывает наш таймер. Это же ёперный театр, как удобно! Можно навесить этот декоратор на что угодно, и всё будет работать.
А что там ещё есть готового, чтобы не изобретать велосипед? О, тут целая куча полезных штук, которые уже стоят в гараже, заправленные и готовые к бою.
@staticmethod,@classmethod,@property— это классика для работы с классами. Без них ни один уважающий себя ООП-шник на работу не выйдет.@functools.lru_cache(maxsize=128)— вот это, блядь, мощнейшая штука. Она запоминает результаты вызовов функции для определённых аргументов. Второй раз с теми же аргументами — бац, и результат из кэша, а не вычисляется заново. Производительность взлетает, как ужаленная. Особенно для рекурсивных или тяжелых функций.@dataclasses.dataclass(Python 3.7+) — это вообще песня. Хочешь создать класс, который в основном хранит данные? Раньше надо было писать тонну boilerplate-кода:__init__,__repr__,__eq__. А теперь навесил этот декоратор — и он всё сгенерирует сам. Удивление пиздец, как же раньше жили-то.
Короче, декораторы — это одна из тех фишек Python, после понимания которой хочется сказать «ни хуя себе» и начать использовать её везде. Главное — без фанатизма, а то можно так задекорировать, что и сам потом не разберёшься, что там под капотом творится.