如何用可调用类修饰类方法?

时间:2019-07-05 14:17:06

标签: python decorator python-decorators callable

我希望类中的函数将其返回值存储在某些数据结构中。为此,我想使用装饰器:

results = []
instances = []

class A:
    def __init__(self, data):
        self.data = data

    @decorator
    def f1(self, a, b):
        return self.data + a + b

    @decorator
    def f2(self, a):
        return self.data + a + 1

x = A(1)
x.f1(1, 2)
x.f2(3)

print(results)

问题是,如何实现此装饰器。

主要思想如下:

class Wrapper:

    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        res = self.func(*args)
        results.append(res)
        instances.append(args[0])

def decorator(func):
    return Wrapper(func)

但是随后我收到一条错误消息:

TypeError: f1() missing 1 required positional argument: 'b'

此问题与其他人的问题(How to decorate a method inside a class?Python functools.wraps equivalent for classes)类似,但在我的情况下,放置@functools.wraps或致电@functools.update_wrapped()的位置尚不清楚

2 个答案:

答案 0 :(得分:3)

我想这就是你要的。

In [8]: from functools import wraps 
   ...: results = [] 
   ...: instances = [] 
   ...:  
   ...: class A: 
   ...:     def __init__(self, data): 
   ...:         self.data = data 
   ...:  
   ...:     def decorator(f): 
   ...:         @wraps(f) 
   ...:         def inner(*args, **kwargs): 
   ...:             retval = f(*args, **kwargs) 
   ...:             results.append(retval) 
   ...:             return retval  
   ...:         return inner 
   ...:         
   ...:  
   ...:     @decorator 
   ...:     def f1(self, a, b): 
   ...:         return self.data + a + b 
   ...:  
   ...:     @decorator 
   ...:     def f2(self, a): 
   ...:         return self.data + a + 1 
   ...:                                                                                         

In [9]: a = A(3)                                                                                

In [10]: a.f1(2,3)                                                                              
Out[10]: 8

In [11]: results                                                                                
Out[11]: [8]

Decorator不必在类中,但是在您需要访问类实例本身时,它很方便。您可以将其取出而无需更改任何代码。

In [8]: from functools import wraps 
   ...: results = [] 
   ...: instances = [] 

   ...: def decorator(f): 
   ...:         @wraps(f) 
   ...:         def inner(*args, **kwargs): 
   ...:             retval = f(*args, **kwargs) 
   ...:             results.append(retval) 
   ...:             return retval  
   ...:         return inner 
   ...:  
   ...: class A: 
   ...:     def __init__(self, data): 
   ...:         self.data = data 
   ...:  
   ...:         
   ...:  
   ...:     @decorator 
   ...:     def f1(self, a, b): 
   ...:         return self.data + a + b 
   ...:  
   ...:     @decorator 
   ...:     def f2(self, a): 
   ...:         return self.data + a + 1 
   ...:                                                                                         

In [9]: a = A(3)                                                                                

In [10]: a.f1(2,3)                                                                              
Out[10]: 8

In [11]: results                                                                                
Out[11]: [8]

将起作用。

答案 1 :(得分:3)

如果您想在课堂外有Wrapper,可以实现__get__方法(descriptor protocol):

from functools import partial

class Wrapper:
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, type=None):
        bound_f = partial(self.__call__, obj)
        return bound_f

    def __call__(self, other_self, *args, **kwargs):
        res = self.func(other_self, *args, **kwargs)
        results.append(res)
        return res

def decorator(func):
    return Wrapper(func)

results = []

class A:
    def __init__(self, data):
        self.data = data

    @decorator
    def f1(self, a, b):
        return self.data + a + b

    @decorator
    def f2(self, a):
        return self.data + a + 1

x = A(1)
x.f1(1, 2)
x.f2(3)

print(results)

打印:

[4, 5]