Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Создание декоратора из класса в Python
Да, абсолютно можно создать декоратор из класса! Это мощный и часто используемый паттерн в Python. Декоратор-класс может быть даже более гибким и удобным, чем декоратор-функция, особенно когда нужна сложная логика.
Как это работает: принципы
Декоратор (из класса или функции) должен быть callable и принимать в качестве аргумента функцию или метод. Класс становится callable благодаря методу __call__.
Базовый пример
class LoggerDecorator:
def __init__(self, func):
"""Инициализация с декорируемой функцией"""
self.func = func
def __call__(self, *args, **kwargs):
"""Вызывается при вызове декорированной функции"""
print(f"Вызов функции: {self.func.__name__}")
print(f"Аргументы: {args}, Kwargs: {kwargs}")
result = self.func(*args, **kwargs)
print(f"Результат: {result}")
return result
@LoggerDecorator
def greet(name):
return f"Hello, {name}!"
greet("Alice")
# Выведет:
# Вызов функции: greet
# Аргументы: (Alice,), Kwargs: {}
# Hello, Alice!
# Результат: Hello, Alice!
Декоратор с параметрами
Декоратор-класс может принимать параметры при создании, что очень удобно:
class TimerDecorator:
def __init__(self, repeats=1):
"""Параметр декоратора"""
self.repeats = repeats
def __call__(self, func):
"""Возвращает wrapper функцию"""
def wrapper(*args, **kwargs):
import time
for i in range(self.repeats):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"Повтор {i+1}: {elapsed:.4f} сек")
return result
return wrapper
@TimerDecorator(repeats=3)
def slow_function():
import time
time.sleep(0.1)
return "Done"
slow_function()
# Выведет время три раза
Сохранение метаданных функции
Важный момент: при использовании класса нужно сохранять оригинальные метаданные функции с помощью functools.wraps:
import functools
class RobustDecorator:
def __init__(self, func):
self.func = func
# Копируем метаданные оригинальной функции
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
print(f"Выполняю: {self.func.__name__}")
return self.func(*args, **kwargs)
@RobustDecorator
def process_data(data):
"""Процессит данные"""
return len(data)
print(process_data.__name__) # process_data (сохранено!)
print(process_data.__doc__) # Процессит данные
Пример: декоратор кеширования
class CacheDecorator:
def __init__(self, func):
self.func = func
self.cache = {} # Хранилище результатов
functools.update_wrapper(self, func)
def __call__(self, *args):
if args in self.cache:
print(f"Возврат из кеша для {args}")
return self.cache[args]
result = self.func(*args)
self.cache[args] = result
print(f"Вычислено для {args}")
return result
def clear_cache(self):
"""Дополнительный метод для очистки кеша"""
self.cache.clear()
@CacheDecorator
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
result = fibonacci(5)
fibonacci.clear_cache() # Можем вызвать методы декоратора-класса!
Пример: декоратор валидации
class ValidateDecorator:
def __init__(self, validator_func):
self.validator_func = validator_func
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Валидируем аргументы перед вызовом
if not self.validator_func(*args, **kwargs):
raise ValueError(f"Валидация не пройдена для {func.__name__}")
return func(*args, **kwargs)
return wrapper
def validate_positive(x):
return x > 0
@ValidateDecorator(validate_positive)
def square(x):
return x ** 2
print(square(5)) # 25
print(square(-5)) # ValueError
Декоратор класса (не функции)
Декоратор-класс может украшать и другие классы:
class SingletonDecorator:
_instances = {}
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
if self.cls not in self._instances:
self._instances[self.cls] = self.cls(*args, **kwargs)
return self._instances[self.cls]
@SingletonDecorator
class Database:
def __init__(self):
self.connection = None
def connect(self):
print("Подключение к БД")
db1 = Database()
db2 = Database()
print(db1 is db2) # True — один и тот же объект
Сравнение: класс vs функция
| Аспект | Функция-декоратор | Класс-декоратор |
|---|---|---|
| Простота | Проще для простых случаев | Более структурирован |
| Состояние | Через closure | Через атрибуты класса |
| Методы | Только функция | Может иметь методы и свойства |
| Читаемость | Может быть запутанным с параметрами | Явнее и структурированнее |
| Производительность | Немного быстрее | Немного медленнее (объект) |
| Расширяемость | Сложнее расширять | Легче наследовать и расширять |
Практический пример: логирование с уровнями
import functools
from enum import Enum
class LogLevel(Enum):
DEBUG = 1
INFO = 2
WARNING = 3
class LoggingDecorator:
def __init__(self, level=LogLevel.INFO, prefix=""):
self.level = level
self.prefix = prefix
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
log_msg = f"[{self.level.name}] {self.prefix} Вызов {func.__name__}"
if self.level == LogLevel.DEBUG:
print(f"{log_msg} | Args: {args}, Kwargs: {kwargs}")
else:
print(log_msg)
result = func(*args, **kwargs)
print(f"[{self.level.name}] {self.prefix} Возврат из {func.__name__}")
return result
return wrapper
@LoggingDecorator(level=LogLevel.DEBUG, prefix="[API]")
def api_call(endpoint, data):
return {"status": "success", "data": data}
api_call("/users", {"name": "John"})
Итог
Декораторы из классов — это практичный и мощный инструмент для:
- Сложной логики с состоянием
- Параметризируемых декораторов
- Когда нужны дополнительные методы (как
clear_cache()) - Наследования и расширения функциональности
Выбор между функцией и классом зависит от сложности задачи: для простых случаев хватит функции, для сложных и многоразовых — класс удобнее.