带有类方法的Python装饰器

问题描述 投票:0回答:1

我想要一个装饰器,我可以将其用作@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 python-decorators self python-class
1个回答
0
投票

为了复制实例方法的标准装饰,您需要装饰绑定到该方法的底层 function,而不是方法本身 - 这是因为

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)
© www.soinside.com 2019 - 2024. All rights reserved.