Привет! В прошлых статьях мы научились создавать простые декораторы. Но что, если нужно передать в декоратор параметры? Например, количество повторений, время задержки или уровень логирования? Здесь нам понадобятся декораторы с аргументами.
В этой статье мы разберём:
- Что такое декораторы с аргументами
- Структура декоратора с аргументами
- Практические примеры
- Отличия от декораторов без аргументов
- Что нужно знать перед началом
- Основная часть
- Структура декоратора с аргументами
- Декоратор с аргументом (повторение)
- Декоратор с задержкой (delay)
- Декоратор с условным выполнением
- Декоратор с несколькими аргументами
- Декоратор с аргументами по умолчанию
- Сравнение с декоратором без аргументов
- Задачи для закрепления
- Нюансы и подводные камни
- Аргументы декоратора vs аргументы функции
- Сохранение метаданных с @wraps
- Синтаксис с аргументами
- Частые ошибки и как их избежать
- Ошибка 1: Забыл вернуть wrapper
- Ошибка 2: Неправильный порядок декораторов
- Ошибка 3: Аргументы по умолчанию изменяемые
- Шпаргалка
- Заключение / Выводы
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный Python
- Базовое понимание декораторов (статьи 1–3 в этом подменю)
Совет: Декораторы с аргументами — это декораторы, которые принимают параметры и возвращают настоящий декоратор.
Основная часть
Структура декоратора с аргументами
def decorator_with_args(arg1, arg2):
def decorator(func):
def wrapper(*args, **kwargs):
# ДО вызова
result = func(*args, **kwargs)
# ПОСЛЕ вызова
return result
return wrapper
return decoratorТри уровня:
- Внешняя функция принимает аргументы декоратора
- Средняя функция принимает декорируемую функцию
- Внутренняя функция
wrapperобёртывает вызов
Совет: Это функция, которая возвращает декоратор, который возвращает обёртку.
Декоратор с аргументом (повторение)
from functools import wraps
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello():
print("Привет!")
say_hello()
# Привет!
# Привет!
# Привет!Совет: Аргумент
timesопределяет, сколько раз выполнится функция.
Декоратор с задержкой (delay)
import time
from functools import wraps
def delay(seconds):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
time.sleep(seconds)
return func(*args, **kwargs)
return wrapper
return decorator
@delay(seconds=2)
def slow_message():
print("Сообщение с задержкой в 2 секунды")
slow_message() # Выведет через 2 секундыДекоратор с условным выполнением
def condition(should_run):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if should_run:
return func(*args, **kwargs)
else:
print("Функция отключена")
return None
return wrapper
return decorator
@condition(should_run=True)
def active_function():
print("Функция выполнена")
@condition(should_run=False)
def inactive_function():
print("Это не будет выполнено")
active_function() # Функция выполнена
inactive_function() # Функция отключенаДекоратор с несколькими аргументами
def log(level="INFO"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] Вызов {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log(level="DEBUG")
def debug_function():
print("Отладка")
@log(level="ERROR")
def error_function():
print("Ошибка")
debug_function() # [DEBUG] Вызов debug_function
error_function() # [ERROR] Вызов error_functionСовет: Аргументы декоратора позволяют настраивать его поведение.
Декоратор с аргументами по умолчанию
def retry(times=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
print(f"Попытка {attempt + 1} не удалась: {e}")
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(times=5, delay=0.5)
def unstable_function():
import random
if random.random() < 0.7:
raise ValueError("Ошибка!")
return "Успешно!"
print(unstable_function())Сравнение с декоратором без аргументов
| Тип | Синтаксис | Структура |
|---|---|---|
| Без аргументов | @decorator | def decorator(func): def wrapper(): ... return wrapper |
| С аргументами | @decorator(arg) | def decorator(arg): def inner(func): def wrapper(): ... return inner |
Задачи для закрепления
Задача 1. Напиши декоратор repeat(times), который повторяет функцию times раз.
Задача 2. Напиши декоратор delay(seconds), который добавляет задержку перед выполнением.
Задача 3. Напиши декоратор log(level), который выводит сообщение с указанным уровнем.
Задача 4. Напиши декоратор retry(times, delay), который повторяет функцию при ошибке.
Задача 5. Напиши декоратор condition(should_run), который выполняет функцию только если should_run == True.
Ответы:
Задача 1.
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decoratorЗадача 2.
def delay(seconds):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
time.sleep(seconds)
return func(*args, **kwargs)
return wrapper
return decoratorЗадача 3.
def log(level="INFO"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decoratorЗадача 4.
def retry(times=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Попытка {attempt+1} не удалась: {e}")
time.sleep(delay)
return None
return wrapper
return decoratorЗадача 5.
def condition(should_run):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if should_run:
return func(*args, **kwargs)
return None
return wrapper
return decoratorНюансы и подводные камни
Аргументы декоратора vs аргументы функции
@repeat(times=3) # Аргумент декоратора
def say_hello(name): # Аргумент функции
print(f"Привет, {name}!")Сохранение метаданных с @wraps
from functools import wraps
def repeat(times):
def decorator(func):
@wraps(func) # Сохраняем метаданные
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decoratorСинтаксис с аргументами
Декораторы с аргументами всегда вызываются с круглыми скобками, даже если аргументов нет:
# Ошибка! Аргументы обязательны
@repeat
def func():
pass
# Правильно
@repeat(times=3)
def func():
passЧастые ошибки и как их избежать
Ошибка 1: Забыл вернуть wrapper
def repeat(times):
def decorator(func):
# Забыли return wrapper
return decoratorОшибка 2: Неправильный порядок декораторов
@timer
@repeat(3)
def func():
passОшибка 3: Аргументы по умолчанию изменяемые
# Плохо
def decorator(arg=[]):
passШпаргалка
| Тип декоратора | Синтаксис | Структура |
|---|---|---|
| Без аргументов | @decorator | def decorator(func): ... |
| С аргументами | @decorator(arg) | def decorator(arg): def inner(func): ... |
Заключение / Выводы
Сегодня мы:
- Научились создавать декораторы с аргументами
- Разобрали структуру декораторов с аргументами
- Написали практические примеры
- Поняли отличие от декораторов без аргументов








