【问题标题】:Python decorator as class memberPython 装饰器作为类成员
【发布时间】:2020-10-05 21:21:28
【问题描述】:

我想为类函数创建一个装饰器,它将在函数的开头和结尾生成日志消息,其中包含特定于运行该函数的类实例的信息。我尝试通过将装饰器创建为类成员来做到这一点,但这不起作用,因为装饰器期望该函数作为第一个参数,但作为一个类方法,它需要是self。这是我到目前为止写的不起作用的内容:

import functools, logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s', 
                            datefmt='%Y-%m-%d %I:%M:%S %p',
                            level=logging.DEBUG)

class Foo:
    def __init__(self, bar):
        self.bar = bar
        
    def log(func):
        @functools.wraps(func)
        def wrapper_log(*args, **kwargs):
            logging.info(f"Started (instance {self.bar})")
            func(*args, **kwargs)
            logging.info(f"Finished (instance {self.bar}")
            return func(*args, **kwargs)
        return wrapper_log
        
    @log
    def test(self, a, b, c):
        pass
    
foo = Foo("bar")
foo.test(1, "b", [3, 4])

我不能将它移出类并通过使用@log(self) 装饰类函数将类实例作为参数传递,因为self 在函数之外不存在。我该怎么做?

【问题讨论】:

  • 您调用func 两次 - 在日志之间并返回。 ~ 为什么不把日志调用移到wrapper_log 函数之外呢?我做过类似的事情。虽然我创建了一个没有 functool.wraps()~ 的装饰器。您可能希望保存返回值并返回

标签: python decorator


【解决方案1】:

您可以在类之外定义装饰器,它可以正常工作,但您需要在包装器签名中明确引用self

之所以会这样,是因为函数内部的所有定义仅在函数被调用时才被计算。到函数 test 被调用时,它已经绑定到实例,self 将存在于其命名空间中。

import functools, logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s', 
                            datefmt='%Y-%m-%d %I:%M:%S %p',
                            level=logging.DEBUG)

def log(func):
    @functools.wraps(func)
    def wrapper_log(self, *args, **kwargs):
        logging.info(f"Started (instance {self.bar})")
        func(self, *args, **kwargs)
        logging.info(f"Finished (instance {self.bar}")
        return func(self, *args, **kwargs)
    return wrapper_log

class Foo:
    def __init__(self, bar):
        self.bar = bar
        
    @log
    def test(self, a, b, c):
        pass
    
foo = Foo("bar")
foo.test(1, "b", [3, 4])

这将输出

2020-10-05 11:31:20 PM - INFO - wrapper_log:Started (instance bar)
2020-10-05 11:31:20 PM - INFO - wrapper_log:Finished (instance bar

【讨论】:

  • 谢谢!我注意到日志格式中的 %(funcName) 显示的是包装函数的名称,而不是类中的函数。我认为 @functools.wraps(func) 应该保留原始函数的元数据以进行自省。你知道如何解决这个问题吗?
  • 这是日志模块使用错误名称的错误。 wraps 函数完成了它的工作。更多详情请参考这个答案stackoverflow.com/questions/7003898/…
猜你喜欢
  • 2021-07-16
  • 2021-05-19
  • 2011-04-16
  • 1970-01-01
  • 2010-12-13
  • 2019-02-20
  • 2011-04-10
  • 2021-04-12
  • 2011-04-28
相关资源
最近更新 更多