Три способа создать декоратор для метода класса.
▫️Способ 1. Обычная функция.
Единственное отличие от простого декоратора функции в том, что нужно учитывать аргумент self.
Если же он не нужен то просто пробрасываем его через *args
def decorator_func(func):
def wrapped(*args, **kwargs):
print('decorator_func')
return func(*args, **kwargs)
return wrapped
class MyClass:
@decorator_func
def method(self):
print('call method')
MyClass().method()
# decorator_func
# call method
▫️Способ 2. Методы класса.
Но что, если декоратор жестко привязан к классу и используется только в нём. И стоит задача закрепить декоратор именно за этим классом и расположить внутри него.
В таком случае можно сделать staticmethod. Это будет выглядеть страшно, но работать будет (тестировано на 3.11)
Очевидно, что декоратор должен быть объявлен раньше метода.
class MyClass:
@staticmethod
def decorator(func):
def wrapper(*args, **kwargs):
print('decorator from staticmethod')
return func(*args, **kwargs)
return wrapper
@decorator.__func__
def method(self):
print('method called')
MyClass().method()
# decorator from staticmethod
# method called
Тоже самое будет и с classmethod, но еще хуже.
class MyClass:
@classmethod
def decorator(func):
def wrapper(self, *args, **kwargs):
print('decorator from classmethod')
return func(self, *args, **kwargs)
return wrapper
@decorator.__func__
def method(self):
print('method called')
MyClass().method()
# decorator from classmethod
# method called
Где-то потерялся аргумент cls. Скорее всего это можно решить но лучше не надо. Оба варианта выглядят страшненько 🫣
▫️Способ 3. Вложенный класс и staticmethod
class MyClass:
class deco:
@staticmethod
def my_decorator(func):
def wrapper(*args, **kwargs):
print('decorator from subclass')
return func(*args, **kwargs)
return wrapper
@deco.my_decorator
def method(self):
print('method called')
MyClass().method()
# decorator from subclass
# method called
Получаем чтото вроде микса способов 1 и 2: функция вложена в отдельный класс.
Лучшей практикой является способ 1 - обычные функции.
Всего пару раз за практику я использовал 3й способ, когда декоратор был намертво привязан к классу и нигде больше не мог использоваться (например, отправлял вызов метода на воркера в другой процесс, не спрашивайте почему так, просто так было нужно 🤪)
Способ 2 не советую. Это, скорей, разминка для ума чем практический пример.
PS
- wraps пропустил для краткости
- в коментах дополнительная инфа
#tricks