Descriptor/Decorator challenge
Arnaud Delobelle
arnodel at googlemail.com
Mon Mar 5 09:38:13 EST 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