Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда выполняется декоратор в 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 → при вызове |
| Несколько декораторов | Снизу вверх (от внутреннего к внешнему) |
Ключевой момент: Декоратор — это не магия, это просто функция, которая берёт другую функцию и возвращает обёрнутую версию. Обёртка вызывается при каждом вызове исходной функции, но сам декоратор выполняется один раз при определении.