Descriptors and decorators

Joost Molenaar j.j.molenaar at gmail.com
Mon Oct 25 14:15:19 CEST 2010


WebOb contains this little nugget of code that allows you to define a
decorator that works on both methods and functions:

class Decorator(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, object, type=None):
        if type is None:
            return self
        newfunc = self.func.__get__(object, type)
        return self.__class__(newfunc)

I adapted it into a class, so that I can inherit this functionality
without thinking about it:

class trace(Decorator):
    def __call__(self, *a, **kw):
        print '-->', self.func.__name__, repr(a), repr(kw)
        result = self.func(*a, **kw)
        print '<--', self.func.__name__, '=', repr(result)
        return result

I can then use it like this:

class C(object):
    @trace
    def m(self, x):
        return 2 * x

And like this:

@trace
def f(x):
    return 2 * x

It works:

>>> o = C()
>>> o.m(21)
--> m (21,) {}
<-- m = 42
>>> f(21)
--> f (21,) {}
<-- f = 42

I'm still not sure what Decorator.__get__ does, or at least I'm not
sure enough to be able to explain it well.

Logically, the method C.m is unbound at the time the class is defined
but o.m is bound when it is called. This means that what
Decorator.__init__ receives as its 'func' parameter is the unbound
method C.m, and when it runs it should operate on the bound method o.m
instead. I suspect that this is what happens inside Decorator.__get__:
create a new instance of the decorator, with a bound version of the
decorated method, and call that without needing a 'self' parameter.

Is this in fact the easiest way to explain it?

Joost Molenaar



More information about the Python-list mailing list