3

我试图让我的基于类的装饰器保持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()对装饰函数的调用结果)?
4

1 回答 1

7

特殊方法总是在实例的类型(这里是类对象)上查找,而不是在实例上。否则__repr__,当您尝试打印类本身的表示时,将使用类上的 a;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)

例如,仍然复制__module__,__name____doc__属性,并复制函数中的属性__dict__,但是让任何特殊方法成为代理。

于 2014-02-13T18:30:39.030 回答