← Назад к вопросам

Когда выполняется декоратор?

2.3 Middle🔥 131 комментариев
#DevOps и инфраструктура

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда выполняется декоратор в Python

Декоратор выполняется во время определения функции или класса, а не при их вызове. Это критично понимать для правильного использования декораторов.

1. Базовое исполнение

Декоратор применяется сразу же при определении функции:

print("1. До определения функции")

def my_decorator(func):
    print("2. Декоратор выполняется")
    return func

@my_decorator
def my_function():
    print("3. Функция выполняется")

print("4. После определения")

# Вывод:
# 1. До определения функции
# 2. Декоратор выполняется
# 4. После определения
# (Функция ещё НЕ выполнилась!)

my_function()  # Только здесь выполнится функция
# 3. Функция выполняется

2. Эквивалентный синтаксис

Синтаксис с @ — это сахар для переприсваивания функции:

# Это:
@decorator
def func():
    pass

# Эквивалентно этому:
def func():
    pass
func = decorator(func)  # Декоратор выполняется ЗДЕСЬ

3. Порядок выполнения с несколькими декораторами

Несколько декораторов выполняются снизу вверх (внутри наружу):

def decorator_a(func):
    print("A: Оборачиваю функцию")
    return func

def decorator_b(func):
    print("B: Оборачиваю функцию")
    return func

@decorator_a
@decorator_b
def my_function():
    print("Функция выполняется")

# Вывод при определении:
# B: Оборачиваю функцию
# A: Оборачиваю функцию

my_function()
# Вывод при вызове:
# Функция выполняется

Это эквивалентно:

def my_function():
    pass

my_function = decorator_b(my_function)  # Выполняется первым
my_function = decorator_a(my_function)  # Выполняется вторым

4. Декоратор с параметрами

Декоратор с параметрами имеет ещё один уровень вызова:

def decorator_with_args(prefix=""):
    print(f"1. Создание декоратора с prefix='{prefix}'")
    
    def actual_decorator(func):
        print(f"2. Применение декоратора к {func.__name__}")
        return func
    
    return actual_decorator

print("START")

@decorator_with_args(prefix="[INFO]")
def my_function():
    print("3. Функция выполняется")

print("END")
my_function()

# Вывод:
# START
# 1. Создание декоратора с prefix='[INFO]'
# 2. Применение декоратора к my_function
# END
# 3. Функция выполняется

5. Практический пример: логирование

import time
from functools import wraps

def log_execution(func):
    print(f"[INIT] Регистрирую функцию {func.__name__}")
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[CALL] Вызываю {func.__name__} с args={args}, kwargs={kwargs}")
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"[DONE] {func.__name__} выполнена за {elapsed:.3f}s")
        return result
    
    return wrapper

print("До определения функции")

@log_execution
def slow_function(n):
    time.sleep(0.1)
    return n * 2

print("После определения функции")

result = slow_function(5)
print(f"Результат: {result}")

# Вывод:
# До определения функции
# [INIT] Регистрирую функцию slow_function
# После определения функции
# [CALL] Вызываю slow_function с args=(5,), kwargs={}
# [DONE] slow_function выполнена за 0.101s
# Результат: 10

6. Декоратор классов

Декоратор класса выполняется при определении класса:

def add_repr(cls):
    print(f"Добавляю __repr__ к классу {cls.__name__}")
    
    def __repr__(self):
        return f"{cls.__name__}()"
    
    cls.__repr__ = __repr__
    return cls

print("До определения класса")

@add_repr
class MyClass:
    pass

print("После определения класса")

obj = MyClass()
print(repr(obj))

# Вывод:
# До определения класса
# Добавляю __repr__ к классу MyClass
# После определения класса
# MyClass()

7. Побочные эффекты при определении

Будь осторожен с побочными эффектами декораторов:

resources = []

def register_resource(func):
    print(f"Регистрирую функцию {func.__name__}")
    resources.append(func)  # Побочный эффект
    return func

@register_resource
def handler_a():
    pass

@register_resource
def handler_b():
    pass

print(f"Зарегистрировано {len(resources)} функций")
# Вывод:
# Регистрирую функцию handler_a
# Регистрирую функцию handler_b
# Зарегистрировано 2 функций

# Это используется в Framework (Flask, FastAPI)
app = Flask(__name__)

@app.route("/api/users")  # Регистрирует маршрут ПРИ ОПРЕДЕЛЕНИИ
def get_users():
    return {"users": []}

@app.route("/api/posts")  # Ещё одна регистрация
def get_posts():
    return {"posts": []}

8. Когда выполняется wrapper функция

Важное уточнение: wrapper выполняется при каждом вызове функции:

def my_decorator(func):
    print("ДЕКОРАТОР: выполняется один раз при определении")
    
    def wrapper():
        print("WRAPPER: выполняется каждый раз при вызове")
        return func()
    
    return wrapper

print("START")

@my_decorator
def my_function():
    print("ФУНКЦИЯ: выполняется при вызове")

print("\nПервый вызов:")
my_function()

print("\nВторой вызов:")
my_function()

# Вывод:
# START
# ДЕКОРАТОР: выполняется один раз при определении
#
# Первый вызов:
# WRAPPER: выполняется каждый раз при вызове
# ФУНКЦИЯ: выполняется при вызове
#
# Второй вызов:
# WRAPPER: выполняется каждый раз при вызове
# ФУНКЦИЯ: выполняется при вызове

Резюме

ЭтапКогда выполняется
Декоратор (внешняя функция)При определении функции/класса
Wrapper функцияПри каждом вызове функции
Декоратор с параметрами1. Параметры → при определении, 2. Wrapper → при вызове
Несколько декораторовСнизу вверх (от внутреннего к внешнему)

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

Когда выполняется декоратор? | PrepBro