Как работают декораторы? Можно ли навешивать несколько декораторов на одну функцию?

Декоратор — это функция (или любой вызываемый объект), которая принимает другую функцию, оборачивает её дополнительной логикой и возвращает новую функцию‑обёртку. Оператор @decorator — синтаксический сахар для вызова вида func = decorator(func). На одну функцию можно повесить несколько декораторов: применяются они снизу вверх, а выполняются как вложенные обёртки — от внешнего к внутреннему.
Подробный ответ

Базовая идея декоратора: «взять готовую функцию и добавить к ней поведение, не меняя её исходный код».

def log_call(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} finished")
        return result
    return wrapper

@log_call
def process_order(order_id):
    print(f"Processing {order_id}")

Строка @log_call эквивалентна записи:

process_order = log_call(process_order)

То есть декоратор:

  1. принимает исходную функцию func;

  2. создаёт wrapper, который вызывает func и добавляет нужную логику до/после/вокруг вызова;

  3. возвращает wrapper вместо оригинальной функции.

В backend‑коде через декораторы часто делают логирование, кеширование, авторизацию, ретраи и т.п.

Можно ли навешивать несколько декораторов?

Да, можно. Python позволяет «сложить» несколько декораторов над одной функцией:

@auth_required
@log_call
def get_user_profile(user_id):
    ...

# Эквивалентно:
def get_user_profile(user_id):
    ...

get_user_profile = auth_required(log_call(get_user_profile))

Порядок такой: сначала log_call оборачивает исходную функцию, потом auth_required оборачивает результат log_call.

Порядок применения и выполнения

Важно различать две вещи:

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

Выполнение при вызове: работают как матрёшка — сначала вызывается внешний декоратор (верхний в списке), затем внутренний, и в конце — сама функция.

def deco1(func):
    def wrapper(*args, **kwargs):
        print("deco1 before")
        result = func(*args, **kwargs)
        print("deco1 after")
        return result
    return wrapper

def deco2(func):
    def wrapper(*args, **kwargs):
        print("deco2 before")
        result = func(*args, **kwargs)
        print("deco2 after")
        return result
    return wrapper

@deco1
@deco2
def f():
    print("func")

f()

Вывод будет:

deco1 before
deco2 before
func
deco2 after
deco1 after

Здесь deco2 ближе к функции, а deco1 — внешняя обёртка.

Фраза для собеседования

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

Оцени свой прогресс

Честно оцени своё понимание этого вопроса, чтобы мы могли построить твой учебный трек максимально эффективно.
Читать в блоге