【问题标题】:How to Access the Decorated Class' Attributes from a Class Decorator through `functools.wraps`如何通过`functools.wraps`从类装饰器访问装饰类的属性
【发布时间】:2019-09-05 15:40:07
【问题描述】:

我正在关注David Beazley's Python Cookbook 的类装饰器配方,这似乎更合适,因为它使用functools.wraps 为装饰器提供更好的组织和属性。

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

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'

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

【问题讨论】:

    标签: python decorator python-decorators


    【解决方案1】:

    您正在将装饰器应用于实例方法,因此该装饰方法绑定的实例将作为第一个参数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
    

    【讨论】:

    • 非常优雅和简单的解决方案。非常感谢!
    【解决方案2】:

    “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()
    

    【讨论】:

    • 您的答案与@blhsing 基本相同,但他在您之前大约11 分钟发布了它。所以,不幸的是,我会给他加分。至少我会赞成你的回答!
    猜你喜欢
    • 2015-04-21
    • 2014-02-08
    • 2021-12-04
    • 2019-08-08
    • 1970-01-01
    • 2019-08-19
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    相关资源
    最近更新 更多