Декораторы Python с методами класса

Мне нужен декоратор, который я могу использовать как @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)

Причина, по которой я хочу использовать функцию декоратора в качестве двух приведенных выше представлений, заключается в следующем:

  1. Я хочу использовать @decorator_with_args, когда аргумент известен, например @decorator_with_args('World')
  2. Я хочу использовать Decorator_with_args(), когда аргумент неизвестен, и я хочу, чтобы аргумент был динамическим, например, Decorator_with_args(f'World{a}')

Хотя @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'.

Я понимаю, что могут существовать и другие подобные вопросы, которые я пытался найти, но не смог заставить их работать для моего варианта использования.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Чтобы воспроизвести стандартное оформление метода экземпляра, вам нужно декорировать базовую функцию, привязанную к этому методу, а не сам метод — это потому, что 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 аргумента:

  • «я» из связанного метода
  • «я» из вызова функции в do_add
  • а
  • б

Если вы хотите получить доступ к функции из связанного метода, вы можете сделать следующее:

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

Другие вопросы по теме