← Назад к вопросам

Можно ли создать декоратор из класса?

2.0 Middle🔥 111 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Создание декоратора из класса в 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())
  • Наследования и расширения функциональности

Выбор между функцией и классом зависит от сложности задачи: для простых случаев хватит функции, для сложных и многоразовых — класс удобнее.