Descriptor/Decorator challenge

Arnaud Delobelle arnodel at googlemail.com
Mon Mar 5 15:38:13 CET 2007


On 5 Mar, 07:31, "Raymond Hettinger" <pyt... at rcn.com> wrote:
> I had an idea but no time to think it through.
> Perhaps the under-under name mangling trick
> can be replaced (in Py3.0) with a suitably designed decorator.
> Your challenge is to write the decorator.
> Any trick in the book (metaclasses, descriptors, etc) is fair game.

I had some time this lunchtimes and I needed to calm my nerves so I
took up your challenge :)
Here is my poor effort.  I'm sure lots of things are wrong with it but
I'm not sure I'll look at it again.

from types import MethodType, FunctionType

# The suggested localmethod decorator
class localmethod(object):
    def __init__(self, f):
        self.f = f
        self.defclass = None
        self.nextmethod = None
    def __get__(self, obj, objtype=None):
        callobj = obj or objtype
        if callobj.callerclass == self.defclass:
            return MethodType(self.f, obj, objtype)
        elif self.nextmethod:
            return self.nextmethod.__get__(obj, objtype)
        else:
            raise AttributeError

class BoundMethod(object):
    def __init__(self, meth, callobj, callerclass):
        self.meth = meth
        self.callobj = callobj
        self.callerclass = callerclass
    def __call__(self, *args, **kwargs):
        callobj = self.callobj
        try:
            callobj.callerclass = self.callerclass
            return self.meth(*args, **kwargs)
        finally:
            callobj.callerclass = None

# A 'mormal' method decorator is needed as well
class method(object):
    def __init__(self, f):
        self.f = f
        self.defclass = None
    def __get__(self, obj, objtype=None):
        callobj = obj or objtype
        return BoundMethod(MethodType(self.f, obj, objtype), callobj,
self.defclass)

class Type(type):
    def __new__(self, name, bases, attrs):
        for attr, val in attrs.items():
            if type(val) == FunctionType:
                attrs[attr] = method(val)
        return type.__new__(self, name, bases, attrs)
    def __init__(self, name, bases, attrs):
        for attr, val in attrs.iteritems():
            if type(val) == localmethod:
                val.defclass = self
                for base in self.mro()[1:]:
                    if attr in base.__dict__:
                        nextmethod = base.__dict__[attr]
                        val.nextmethod = nextmethod
                        break
            elif type(val) == method:
                val.defclass = self


class Object(object):
    __metaclass__ = Type
    # Note: any class or object has to have a callerclass attribute
for this to work.
    # That makes it thread - incompatible I guess.
    callerclass = None

# Here is your example code

class A(Object):
    @localmethod
    def m(self):
        print 'A.m'
    def am(self):
        self.m()

class B(A):
    @localmethod
    def m(self):
        print 'B.m'
    def bm(self):
        self.m()

m = B()
m.am()     # prints 'A.m'
m.bm()     # prints 'B.m'

# Untested beyond this particular example!

--
Arnaud




More information about the Python-list mailing list