【问题标题】:How to decorate a class method with a callable class?如何用可调用类装饰类方法?
【发布时间】:2019-11-16 04:34:58
【问题描述】:

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

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 个 A 实例,我们称它们为 xy,您是否希望它们写入同一个列表?
  • 我的问题是所有实例写入同一个列表的方式。实际上,我对注册类实例也更感兴趣。我更新了我的问题。

标签: python decorator python-decorators callable


【解决方案1】:

我想这就是你想要的。

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]

装饰器不必在类中,但当您需要访问类实例本身时它很方便。无需更改任何代码即可将其取出。

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]

也一样。

【讨论】:

    【解决方案2】:

    如果你想让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]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-14
      • 1970-01-01
      • 2020-01-11
      • 2019-11-06
      • 2019-02-03
      相关资源
      最近更新 更多