代码之家  ›  专栏  ›  技术社区  ›  Prasun Kumar Khan

具有类方法的Python装饰器

  •  0
  • Prasun Kumar Khan  · 技术社区  · 1 年前

    我想要一个decorator,它可以用作类方法的@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函数作为上述两种表示,是因为:

    1. 当参数已知时,我想使用@decorator_with_args,例如@decorator _with_arg('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' .

    我很感激可能存在其他类似的问题,我试图搜索这些问题,但无法使它们适用于我的用例。

    2 回复  |  直到 1 年前
        1
  •  3
  •   matszwecja    1 年前

    为了复制实例方法的标准装饰,您需要装饰底层 作用 这与该方法有关,而不是与方法本身有关——这是因为 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)
    
        2
  •  0
  •   Marco Bresson    1 年前

    如果添加 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中函数调用的“self”
    • b

    如果要从绑定方法访问函数,可以执行以下操作:

    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