← Назад к вопросам
Что такое декоратор с параметром?
1.8 Middle🔥 101 комментариев
#Python Core#Soft Skills
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Декоратор с параметром
Декоратор с параметром (parameterized decorator) — это функция, которая принимает аргументы для конфигурации поведения, а затем возвращает сам декоратор. Это позволяет настраивать, как декоратор обрабатывает функцию или метод.
Различие между обычным и параметризованным декоратором
Обычный декоратор (без параметров)
# Простой декоратор без параметров
def simple_decorator(func):
"""Оборачивает функцию логированием"""
def wrapper(*args, **kwargs):
print(f"Вызов функции {func.__name__}")
return func(*args, **kwargs)
return wrapper
@simple_decorator
def greet(name: str):
return f"Hello, {name}!"
print(greet("Alice")) # Выводит "Вызов функции greet"
Параметризованный декоратор
# Декоратор принимает параметры
def parametrized_decorator(prefix: str):
"""Декоратор с параметром prefix"""
def decorator(func):
"""Это уже сам декоратор"""
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return f"{prefix}: {result}"
return wrapper
return decorator
# Использование с параметром
@parametrized_decorator(prefix=">>> ")
def greet(name: str):
return f"Hello, {name}!"
print(greet("Alice")) # >>> Hello, Alice!
Структура декоратора с параметрами
# Трёхуровневая вложенность:
# 1. Функция параметров
# 2. Сам декоратор
# 3. Обёртка функции
def outer_parameters(param1, param2):
"""Уровень 1: принимает параметры конфигурации"""
print(f"Инициализация декоратора: param1={param1}, param2={param2}")
def decorator(func):
"""Уровень 2: сам декоратор"""
print(f"Применение декоратора к функции {func.__name__}")
def wrapper(*args, **kwargs):
"""Уровень 3: обёртка функции"""
print(f"Выполнение функции с параметрами {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
return decorator
# Применение
@outer_parameters("config1", "config2")
def my_function(x):
return x * 2
my_function(5)
# Вывод:
# Инициализация декоратора: param1=config1, param2=config2
# Применение декоратора к функции my_function
# Выполнение функции с параметрами (5,), {}
Практические примеры
Пример 1: Retry декоратор с параметрами
import time
import functools
from typing import Callable
def retry(max_attempts: int = 3, delay: float = 1.0):
"""Декоратор для повтора функции при ошибке"""
def decorator(func: Callable):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_error = None
for attempt in range(1, max_attempts + 1):
try:
print(f"Попытка {attempt}/{max_attempts}")
return func(*args, **kwargs)
except Exception as e:
last_error = e
print(f"Ошибка: {e}")
if attempt < max_attempts:
print(f"Ждём {delay} сек перед повтором...")
time.sleep(delay)
raise last_error
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def unstable_api_call():
import random
if random.random() < 0.7:
raise ConnectionError("API недоступен")
return "Успешно!"
try:
result = unstable_api_call()
print(result)
except Exception as e:
print(f"Всё попыткам исчерпаны: {e}")
Пример 2: Rate limiting декоратор
import time
from functools import wraps
def rate_limit(max_calls: int, time_window: float):
"""Ограничивает количество вызовов в единицу времени"""
def decorator(func):
calls = []
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
# Удаляем старые вызовы
calls[:] = [call_time for call_time in calls
if call_time > now - time_window]
if len(calls) >= max_calls:
raise RuntimeError(
f"Превышено {max_calls} вызовов за {time_window} сек"
)
calls.append(now)
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(max_calls=5, time_window=10)
def api_endpoint():
return "API ответ"
# Вызовем 5 раз - OK
for i in range(5):
print(api_endpoint())
# Шестой вызов вызовет ошибку
try:
api_endpoint()
except RuntimeError as e:
print(f"Ошибка: {e}")
Пример 3: Валидация аргументов
from typing import Callable, get_type_hints
from functools import wraps
def validate_types(**type_checks):
"""Валидирует типы аргументов функции"""
def decorator(func: Callable):
@wraps(func)
def wrapper(*args, **kwargs):
# Получить имена параметров функции
import inspect
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
# Проверить типы
for arg_name, expected_type in type_checks.items():
if arg_name in bound_args.arguments:
value = bound_args.arguments[arg_name]
if not isinstance(value, expected_type):
raise TypeError(
f"Аргумент {arg_name} должен быть "
f"{expected_type.__name__}, "
f"получено {type(value).__name__}"
)
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(name=str, age=int)
def create_user(name: str, age: int):
return f"User: {name}, Age: {age}"
print(create_user("Alice", 25)) # OK
try:
create_user("Bob", "30") # Ошибка: age должен быть int
except TypeError as e:
print(f"Ошибка: {e}")
Пример 4: Кэширование с TTL
import time
from functools import wraps
def cache_with_ttl(ttl_seconds: int):
"""Кэширует результат функции на определённое время"""
def decorator(func):
cache = {}
cache_time = {}
@wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
now = time.time()
# Проверить, есть ли кэш и не устарел ли
if key in cache and now - cache_time[key] < ttl_seconds:
print(f"Возвращаю из кэша (возраст: {now - cache_time[key]:.1f}s)")
return cache[key]
# Вычислить результат
print(f"Вычисляю результат для {func.__name__}{args}")
result = func(*args, **kwargs)
# Сохранить в кэш
cache[key] = result
cache_time[key] = now
return result
return wrapper
return decorator
@cache_with_ttl(ttl_seconds=5)
def expensive_computation(n: int):
"""Дорогостоящее вычисление"""
time.sleep(2) # Имитация длительного процесса
return n * n
print(expensive_computation(5)) # Вычисляет (2s)
print(expensive_computation(5)) # Из кэша (0s)
time.sleep(6)
print(expensive_computation(5)) # Кэш устарел, вычисляет (2s)
Пример 5: Логирование с конфигурацией
import functools
import time
from enum import Enum
class LogLevel(Enum):
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
def log_call(level: LogLevel = LogLevel.INFO, prefix: str = ""):
"""Логирует вызовы функции"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
# Логирование входа
log_msg = f"[{level.value}] {prefix} Вызов {func.__name__}"
print(log_msg)
try:
# Выполнение
result = func(*args, **kwargs)
elapsed = time.time() - start
# Логирование выхода
print(f"[{level.value}] {prefix} Завершено за {elapsed:.3f}s")
return result
except Exception as e:
elapsed = time.time() - start
print(f"[ERROR] {prefix} Ошибка за {elapsed:.3f}s: {e}")
raise
return wrapper
return decorator
@log_call(level=LogLevel.DEBUG, prefix="API")
def fetch_data(url: str):
time.sleep(0.5)
return {"status": "success"}
fetch_data("https://example.com")
# Вывод:
# [DEBUG] API Вызов fetch_data
# [DEBUG] API Завершено за 0.501s
Использование functools.wraps
from functools import wraps
# ❌ Без wraps - теряется информация
def bad_decorator(param):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
# ✅ С wraps - сохраняется информация
def good_decorator(param):
def decorator(func):
@wraps(func) # Копирует __name__, __doc__, __annotations__
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
@good_decorator("param")
def my_function():
"""Документация функции"""
pass
print(my_function.__name__) # my_function (без wraps было wrapper)
print(my_function.__doc__) # Документация функции (без wraps было None)
Декоратор с опциональными параметрами
from functools import wraps
from typing import Callable, Optional
def flexible_decorator(func: Optional[Callable] = None, *, enabled: bool = True):
"""Можно использовать с параметрами или без"""
def decorator(f: Callable):
@wraps(f)
def wrapper(*args, **kwargs):
if enabled:
print(f"Выполняю {f.__name__}")
return f(*args, **kwargs)
return wrapper
# Если функция передана напрямую (без параметров)
if func is not None:
return decorator(func)
# Если передана как @flexible_decorator() или @flexible_decorator(enabled=False)
return decorator
# Использование 1: без параметров
@flexible_decorator
def func1():
return "func1"
# Использование 2: с параметрами
@flexible_decorator(enabled=False)
def func2():
return "func2"
func1() # Выводит "Выполняю func1"
func2() # Ничего не выводит
Декораторы с параметрами — мощный инструмент для создания гибких и переиспользуемых решений в Python. Они позволяют написать один декоратор для множества разных случаев использования.