基于类的装饰器和repr()保护

时间:2014-02-13 18:26:53

标签: python python-3.x decorator python-decorators repr

我试图让我的基于类的装饰器保持原始包装函数的repr()行为(以匹配functools.wraps装饰器在函数上的工作方式)。我正在使用python 3.3。

首先我尝试了functools:

import functools

class ClassBasedDecorator():
    def __init__(self, fn):
        self.fn = fn
        functools.update_wrapper(self, fn)
    def __call__(self, *args, **kwargs):
        self.fn(*args, **kwargs)

@ClassBasedDecorator
def wrapped(text):
    pass

但是当我在装饰函数上调用repr()时,我得到:

>>> repr(wrapped)
'<__main__.ClassBasedDecorator object at 0x2d8860b6850>'

很好,所以我尝试自定义我的装饰器的__repr__方法,该方法应由repr()调用。

再次使用functools:

class ClassBasedDecorator():
    def __init__(self, fn):
        self.fn = fn
        functools.update_wrapper(
            self, fn,
            assigned=functools.WRAPPER_ASSIGNMENTS + ('__repr__',)
        )
    def __call__(self, *args, **kwargs):
        self.fn(*args, **kwargs)

不会改变输出,但会发生一些有趣的事情:

>>> repr(wrapped)
'<__main__.ClassBasedDecorator object at 0x2d8860b69d0>'
>>> wrapped.__repr__()
'<function wrapped at 0x2d8860a9710>'

明确设置装饰器实例的__repr__方法具有相同的效果。

经过多次测试后,我推断repr(instance)实际上调用了instance.__class__.__repr__(instance)。因此,永远不会调用实例的重写__repr__方法。


所以这是我的问题:

  • 为什么repr(instance)拨打instance.__class__.__repr__(instance)而不是instance.__repr__()?或者我错过了其他什么?
  • 如何将基于函数的装饰器functools.wraps完全复制到基于类的装饰器(包括更改装饰函数上repr()调用的结果)?

1 个答案:

答案 0 :(得分:6)

特殊方法是always looked up on the type of the instance(这里是类对象),而不是实例。否则,当您尝试打印类本身的表示时,将使用类上的__repr__; type(class).__repr__(class)会使用正确的魔术方法,而class.__repr__()会引发异常,因为未提供self

实施您自己的__repr__挂钩:

class ClassBasedDecorator():
    def __init__(self, fn):
        self.fn = fn
        functools.update_wrapper(self, fn)
    def __call__(self, *args, **kwargs):
        self.fn(*args, **kwargs)
    def __repr__(self):
        return repr(self.fn)

e.g。仍然复制__module____name____doc__属性,并复制函数__dict__中的属性,但将任何特殊方法作为代理。