Как работают декораторы в Python?

Ответ

Декоратор в Python — это функция, которая принимает другую функцию и расширяет её поведение без изменения исходного кода. Декораторы оборачивают целевую функцию, добавляя логику до или после её вызова, либо модифицируя её аргументы или результат.

Почему используются: Декораторы позволяют многократно использовать логику (например, логирование, аутентификация, замер времени) для различных функций, сохраняя код чистым и модульным.

Пример простого декоратора:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("До вызова функции")
        result = func(*args, **kwargs)
        print("После вызова функции")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Привет, {name}!")
    return f"Hello, {name}"

output = say_hello("Мир")
print(output)

Вывод:

До вызова функции
Привет, Мир!
После вызова функции
Hello, Мир

Ключевые моменты:

  • Декоратор принимает функцию как аргумент и возвращает новую функцию (обычно wrapper).
  • Синтаксис @decorator является синтаксическим сахаром для func = decorator(func).
  • Для сохранения метаданных (имени, docstring) оборачиваемой функции используют functools.wraps.
  • wrapper должен принимать *args и **kwargs, чтобы оборачиваемая функция могла принимать любые аргументы.

Частые применения:

  • Логирование: Запись информации о вызовах функций.
  • Замер времени: Измерение времени выполнения функций.
  • Кеширование: Сохранение результатов дорогостоящих вычислений.
  • Проверка прав доступа: Ограничение доступа к функциям.
  • Транзакции БД: Управление транзакциями для функций, работающих с базой данных.

Ответ 18+ 🔞

О, декораторы в питоне, ну это ж классика, блядь! Представь себе такую хуйню: есть у тебя функция, а ты хочешь её обвешать дополнительной логикой, но лезть внутрь и ковырять — лень, да и некрасиво. Вот тут-то декоратор и выручает, сука!

По сути, декоратор — это такая функция-обёртка, которая хватает твою целевую функцию за жопу и говорит: «Стоять, я тебя сейчас улучшу, нахуй!». Она может что-то сделать до вызова, после, или вообще аргументы подменить — полный карт-бланш, ёпта.

Зачем это, спрашивается, нужно? Да чтобы не повторять один и тот же код в каждой функции, блядь! Логирование, замер времени, проверка прав — всё это можно вынести в декоратор и потом просто прилепить сверху, как ярлык «Хрупкое, блядь!».

Смотри, как это выглядит в жизни:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("До вызова функции")
        result = func(*args, **kwargs)
        print("После вызова функции")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Привет, {name}!")
    return f"Hello, {name}"

output = say_hello("Мир")
print(output)

А на выходе получаем:

До вызова функции
Привет, Мир!
После вызова функции
Hello, Мир

Важные моменты, чтобы не обосраться:

  • Декоратор жрёт функцию и выплёвывает новую, обычно это wrapper. Всё просто, как три копейки, блядь.
  • Эта магия с @ — просто синтаксический сахар, чтобы не писать say_hello = my_decorator(say_hello). Красиво, ёпта!
  • Чтобы твоя обёрнутая функция не потеряла имя и докстринг, как последняя мартышлюшка, используй functools.wraps. Иначе отладка превратится в пиздец.
  • wrapper всегда должен принимать *args, **kwargs — а то вдруг твоя функция ждёт нестандартный набор аргументов? Не угадаешь, блядь.

Где это применяют? Да везде, сука! Логируют вызовы, замеряют скорость, кешируют результаты (чтобы два раза не считать одно и то же), проверяют, а не шпион ли ты, и управляют транзакциями в базе. В общем, мощнейший инструмент, если руки не из жопы.