Как украсить метод класса вызываемым классом? - PullRequest
3 голосов
/ 05 июля 2019

Я хочу, чтобы функции в классе хранили свои возвращенные значения в некоторой структуре данных. Для этого я хочу использовать декоратор:

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'

Этот вопрос похож на вопрос других людей ( Как украсить метод внутри класса? , Python functools.wraps, эквивалентный для классов ), но неясно, где поставить @functools.wraps или позвонить @functools.update_wrapped() в моем случае.

Ответы [ 2 ]

3 голосов
/ 05 июля 2019

Если вы хотите, чтобы Wrapper в классе снаружи, вы можете реализовать метод __get__ ( протокол дескриптора ):

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]
3 голосов
/ 05 июля 2019

Я думаю, это то, что вы просите.

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]

будет работать так же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...