Мне нужен декоратор, который я могу использовать как @decorator, так и Decorator() с методами класса, например:
def decorator_with_args(name):
print(f'Hello {name} !')
def decorator(func):
def wrapper(self, *args, **kwargs):
print(f'Hello {self.title} {name} again !!')
print(f"Calling {func.__name__} with instance {self}...")
result = func(self, *args, **kwargs)
print(f"{func.__name__} finished. Result: {result}")
return result
return wrapper
return decorator
class Calculator:
def __init__(self):
self.title = 'Mr'
@decorator_with_args('World')
def add(self, a, b):
return a + b
def add2(self, a, b):
return a + b
def do_add(self, a, b):
return decorator_with_args(f'World{a}')(self.add2)(self, a, b)
# Usage 1
calc = Calculator()
result = calc.add(3, 5)
# Usage 2
calc = Calculator()
result = calc.do_add(3, 5)
Причина, по которой я хочу использовать функцию декоратора в качестве двух приведенных выше представлений, заключается в следующем:
Хотя @decorator_with_args
работает должным образом [Использование 1], я получаю сообщение об ошибке с decorator_with_args()
[Использование 2]. Я пробовал несколько вещей, но ни одна из них не помогла в последнем случае.
Если я попытаюсь пройти (self, a, b)
, например decorator_with_args(f'World{a}')(self.add2)(self, a, b)
, я получу TypeError: add2() takes 3 positional arguments but 4 were given
.
С другой стороны, если я попытаюсь пройти (a, b)
без self
, например decorator_with_args(f'World{a}')(self.add2)(a, b)
, я получу AttributeError: 'int' object has no attribute 'title'
.
Я понимаю, что могут существовать и другие подобные вопросы, которые я пытался найти, но не смог заставить их работать для моего варианта использования.
Чтобы воспроизвести стандартное оформление метода экземпляра, вам нужно декорировать базовую функцию, привязанную к этому методу, а не сам метод — это потому, что self.add2
неявно добавляет self
в качестве первого аргумента к этой функции, поэтому он удваивается.
def do_add(self, a, b):
return decorator_with_args(f'World{a}')(Calculator.add2)(self, a, b)
или
def do_add(self, a, b):
return decorator_with_args(f'World{a}')(self.add2.__func__)(self, a, b)
Если вы добавите print(f"Calling {func} with instance {self}...")
в свою обертку, вы увидите следующее:
# Usage 1
calc = Calculator()
result = calc.add(3, 5)
>>> Calling <function Calculator.add at 0x15815b5b0> with instance <__main__.Calculator object at 0x158174430>...
>>> add finished. Result: 8
# Usage 2
calc = Calculator()
result = calc.do_add(3, 5)
>>> Calling <bound method Calculator.add2 of <__main__.Calculator object at 0x158177100>> with instance <__main__.Calculator object at 0x158177100>...
>>> TypeError...
Это потому, что self.add2
привязан к экземпляру, и поэтому ваша оболочка будет иметь 4 аргумента:
Если вы хотите получить доступ к функции из связанного метода, вы можете сделать следующее:
def do_add(self, a, b):
return decorator_with_args(f'World{a}')(Calculator.add2)(self, a, b)
>>> Hello World3 !
>>> Hello Mr World3 again !!
>>> Calling <function Calculator.add2 at 0x15815b370> with instance <__main__.Calculator object at 0x1581be860>...
>>> <__main__.Calculator object at 0x1581be860> 3 5
>>> add2 finished. Result: 8