如何通过`functools.wraps`从类装饰器中访问“装饰的类”属性

时间:2019-09-05 15:40:07

标签: python decorator python-decorators

我正在遵循David Beazley's Python Cookbook的Class Decorator食谱,该食谱似乎更合适,因为它使用functools.wraps为装饰者提供了更好的组织和属性。

但是,我不清楚如何(或是否)从装饰器本身内部访问实例的属性。这是代码段:

import time
import types
from functools import wraps

class TimeDecorator():

    def __init__(self, func):
        wraps(func)(self)

    def __call__(self, *args, **kwargs):
        tic = time.time()
        func_return = self.__wrapped__(*args, **kwargs)
        tac = time.time()
        total_time = round((tac - tic) / 60, 2)
        print(f'Operation Time: {total_time} min.')
        # print(self.__wrapped__.test_attr) # going to give an error...
        return func_return

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)


class A():

    def __init__(self):
        self.test_attr = 0

    @TimeDecorator
    def do_something(self):
        time.sleep(1) # example of stuff only...

我尝试通过装饰器的__call__self.__wrapped__.test_attrself.__wrapped__.__self__.test_attr访问实例,但是所有人都告诉我:

AttributeError: 'function' object has no attribute 'test_attr'

那么在这种情况下,我将如何访问修饰的类的属性?还是我必须使用另一种方式来构建我的装饰器?

2 个答案:

答案 0 :(得分:2)

您正在将装饰器应用于实例方法,因此将将此装饰方法绑定到的实例作为第一个参数self传入,您可以在其中找到实例属性。重新寻找,因此您只需从以下示例中名为self的参数中提取wrapped_self

def __call__(self, wrapped_self, *args, **kwargs):
    tic = time.time()
    func_return = self.__wrapped__(wrapped_self, *args, **kwargs)
    tac = time.time()
    total_time = round((tac - tic) / 60, 2)
    print(f'Operation Time: {total_time} min.')
    print(wrapped_self.test_attr)
    return func_return

答案 1 :(得分:1)

“ self”是do_something函数的第一个参数。

因此您可以在装饰器中明确标识它。 wrapped将成为修饰的类实例,而*args将包含所有其他位置参数。

from functools import wraps
import types
import time

class TimeDecorator():

    def __init__(self, func):
        wraps(func)(self)

    def __call__(self, wrapped, *args, **kwargs):
        tic = time.time()
        func_return = self.__wrapped__(wrapped, *args, **kwargs)
        tac = time.time()
        total_time = round((tac - tic) / 60, 2)
        print(f'Operation Time: {total_time} min.')
        print(wrapped.test_attr)
        return func_return

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)


class A():

    def __init__(self):
        self.test_attr = 0

    @TimeDecorator
    def do_something(self):
        time.sleep(1) # example of stuff only...



a = A()
a.do_something()