I was trying to have my class-based decorator keeping the repr() behavior of the original wrapped function (to match the way the functools.wraps decorator works on functions). I am using python 3.3.
First I tried 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
But when I call repr() on the decorated function, I get:
>>> repr(wrapped)
'<__main__.ClassBasedDecorator object at 0x2d8860b6850>'
Very well, so I tried to customize the __repr__ method of my decorator, which is supposed to be called by repr().
Using functools again:
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)
Doesn't change the output, but something interesting happens:
>>> repr(wrapped)
'<__main__.ClassBasedDecorator object at 0x2d8860b69d0>'
>>> wrapped.__repr__()
'<function wrapped at 0x2d8860a9710>'
Explicitly setting the __repr__ method of the decorator instance has the same effect.
After a little more tests I deduced repr(instance) actually calls instance.__class__.__repr__(instance). Thus the overriden __repr__ method of the instance is never called.
So here are my questions:
repr(instance) call the instance.__class__.__repr__(instance) instead of instance.__repr__()? Or have I missed something else?functools.wraps does with function-based decorators to class-based decorators (including altering the result of repr() calls on the decorated function)?Special methods are always looked up on the type of the instance (here the class object), not on the instance. Otherwise a __repr__ on a class would be used when you tried to print the representation of the class itself; type(class).__repr__(class) would use the correct magic method, while class.__repr__() would raise an exception because self was not provided.
Implement your own __repr__ hooks:
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. still copy over the __module__, __name__ and __doc__ attributes, and copy over the attributes from the function __dict__, but make any special methods a proxy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With