如何创建一个在使用“functools.wraps”时接受参数的装饰器类并保留对装饰实例的访问

Phi*_*aro 1 python decorator python-decorators

1. 我的要求

  1. 应该使用装饰器类functools.wraps,以便为以后进行适当的内省和组织。
  2. 应该可以访问装饰实例。
    • 在下面的示例中,我通过wrapped_self向该__call__方法传递一个参数来实现这一点。
  3. 正如标题所述,装饰器类必须具有可以为每个方法调整的参数。

2. 其外观示例

理想的情况应该是这样的:

class A():

    def __init__(self):
        ...

    @LoggerDecorator(logger_name='test.log')
    def do_something(self):
        ...
Run Code Online (Sandbox Code Playgroud)

到目前为止,装饰器类是(基于来自David Beazley 的 Python Cookbook 的食谱的基本记录器装饰器):

class LoggerDecorator():

    def __init__(self, func, logger_name):
        wraps(func)(self)
        self.logger_name = logger_name

    def config_logger(self):
        ... # for example, uses `self.logger_name` to configure the decorator

    def __call__(self, wrapped_self, *args, **kwargs):
        self.config_logger()
        wrapped_self.logger = self.logger
        func_to_return = self.__wrapped__(wrapped_self, *args, **kwargs)
        return func_to_return

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)
Run Code Online (Sandbox Code Playgroud)

3. 如何解决?

我收到的错误是指__init__显然没有识别第三个参数:

TypeError: __init__() missing 1 required positional argument: 'func'
Run Code Online (Sandbox Code Playgroud)

有人建议我应该加入func__call__方法。但是,如果我将其作为参数放在那里,则wrapped_self无法正确读取为参数,并且出现此错误:

__call__() missing 1 required positional argument: 'wrapped_self'
Run Code Online (Sandbox Code Playgroud)

我尝试了很多方法来解决这个问题,wraps(func)(self)包括__call__:以及这个非常接近但不能完全满足所有需求解决方案的许多变体(问题是我似乎无法再访问wrapped_self)。

blh*_*ing 6

由于您正在实现一个接受参数的装饰器,因此 的__init__方法LoggerDecorator应该只接受配置装饰器的参数,而该__call__方法应该成为返回包装函数的实际装饰器:

class LoggerDecorator():
    def __init__(self, logger_name):
        self.logger_name = logger_name
        self.config_logger()

    def __call__(self, func):
        @wraps(func)
        def wrapper(wrapped_self, *args, **kwargs):
            wrapped_self.logger = self.logger
            func_to_return = func(wrapped_self, *args, **kwargs)
            return func_to_return
        return wrapper
Run Code Online (Sandbox Code Playgroud)