Что такое фабрика декораторов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Фабрика декораторов
Фабрика декораторов — это паттерн проектирования, который позволяет создавать и применять декораторы параметризованным способом. Фабрика производит готовые декораторы на основе переданных параметров, вместо прямого использования одного и того же декоратора.
Основная идея
Вместо статичного декоратора, который всегда делает одно и то же, фабрика позволяет создать декоратор с определёнными параметрами. Это особенно полезно, когда нужна переиспользуемая логика декорирования с разными конфигурациями.
Базовый пример: простой декоратор vs фабрика
# ПРОСТОЙ ДЕКОРАТОР - одна конфигурация
def timer_decorator(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Время выполнения: {end - start:.2f}с")
return result
return wrapper
@timer_decorator
def process_data():
import time
time.sleep(1)
# ФАБРИКА ДЕКОРАТОРОВ - параметризованная
def timer_factory(precision=2):
"""Фабрика создаёт декоратор с параметром precision"""
def timer_decorator(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Время: {end - start:.{precision}f}с")
return result
return wrapper
return timer_decorator
# Теперь можно использовать с разной точностью
@timer_factory(precision=2)
def fast_task():
import time
time.sleep(0.1)
@timer_factory(precision=4)
def slow_task():
import time
time.sleep(1)
Пример: фабрика декораторов для логирования
import logging
from functools import wraps
from typing import Callable, Any
def logging_factory(level=logging.INFO, prefix=""):
"""Фабрика создаёт декоратор логирования с параметрами"""
def logging_decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
logger = logging.getLogger(func.__module__)
log_msg = f"{prefix} Вызов {func.__name__} с args={args}, kwargs={kwargs}"
logger.log(level, log_msg)
try:
result = func(*args, **kwargs)
logger.log(level, f"{prefix} {func.__name__} вернул {result}")
return result
except Exception as e:
logger.error(f"{prefix} {func.__name__} выбросил {type(e).__name__}: {e}")
raise
return wrapper
return logging_decorator
# Использование с разными параметрами
@logging_factory(level=logging.DEBUG, prefix="[DB]")
def database_operation():
return "Данные загружены"
@logging_factory(level=logging.ERROR, prefix="[AUTH]")
def authenticate(username):
return True
database_operation()
authenticate("john")
Пример: фабрика декораторов для валидации
from functools import wraps
from typing import Callable, Any, List
def validate_factory(arg_types: dict):
"""Фабрика создаёт декоратор валидации типов"""
def validator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
# Проверяем kwarg типы
for param, expected_type in arg_types.items():
if param in kwargs:
value = kwargs[param]
if not isinstance(value, expected_type):
raise TypeError(
f"Параметр {param} должен быть {expected_type.__name__}, "
f"а получен {type(value).__name__}"
)
return func(*args, **kwargs)
return wrapper
return validator
@validate_factory({"age": int, "email": str, "scores": list})
def create_user(age, email, scores):
return f"Пользователь {email} создан"
# Работает
print(create_user(age=25, email="user@example.com", scores=[90, 85, 88]))
# Ошибка валидации
try:
create_user(age="twenty-five", email="user@example.com", scores=[90])
except TypeError as e:
print(f"Ошибка: {e}")
Пример: фабрика для кэширования
from functools import wraps
from typing import Callable, Any
import time
def cache_factory(ttl: int = 60):
"""Фабрика создаёт декоратор с TTL (время жизни кэша)"""
def cache_decorator(func: Callable) -> Callable:
cache = {}
timestamps = {}
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
key = (args, tuple(sorted(kwargs.items())))
current_time = time.time()
# Проверяем, есть ли кэш и не истёк ли его TTL
if key in cache and current_time - timestamps[key] < ttl:
print(f"Возврат из кэша для {func.__name__}")
return cache[key]
# Вычисляем результат
result = func(*args, **kwargs)
cache[key] = result
timestamps[key] = current_time
return result
return wrapper
return cache_decorator
@cache_factory(ttl=5)
def expensive_calculation(x, y):
print(f"Вычисляю {x} + {y}...")
time.sleep(1) # Имитация долгого расчёта
return x + y
print(expensive_calculation(5, 10)) # Вычисляет
print(expensive_calculation(5, 10)) # Из кэша
time.sleep(6) # Ждём TTL
print(expensive_calculation(5, 10)) # Снова вычисляет (кэш истёк)
Пример: фабрика для retry логики
from functools import wraps
import time
from typing import Callable, Type
def retry_factory(max_attempts: int = 3, delay: float = 1.0, exceptions: tuple = (Exception,)):
"""Фабрика создаёт декоратор повторных попыток"""
def retry_decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_attempts:
raise
print(f"Попытка {attempt}/{max_attempts} не удалась: {e}. "
f"Повтор через {delay}с...")
time.sleep(delay)
return wrapper
return retry_decorator
@retry_factory(max_attempts=3, delay=0.5, exceptions=(ValueError, ConnectionError))
def unreliable_api_call(url):
import random
if random.random() < 0.7:
raise ConnectionError("Сервер не ответил")
return f"Данные с {url}"
try:
print(unreliable_api_call("https://api.example.com"))
except ConnectionError:
print("Все попытки исчерпаны")
Практический пример: комбинирование фабрик
def combined_example():
# Один метод может использовать несколько фабрик
@retry_factory(max_attempts=2)
@cache_factory(ttl=10)
@logging_factory(prefix="[API]")
def fetch_user_data(user_id: int):
print(f"Загрузка данных пользователя {user_id}...")
return {"id": user_id, "name": "John"}
print(fetch_user_data(1))
print(fetch_user_data(1)) # Из кэша
Преимущества фабрики декораторов
- Переиспользуемость: Один код создаёт много вариаций
- Гибкость: Параметры передаются в момент создания
- Читаемость: Явно указаны конфигурации
- Композиция: Можно комбинировать несколько фабрик
- Тестируемость: Легче тестировать разные варианты
Вывод
Фабрика декораторов — это мощный паттерн для создания переиспользуемых и параметризованных декораторов. Она особенно полезна в больших системах, где нужна гибкость в применении одной и той же логики с разными конфигурациями.