Decorator metaclass

Maric Michaud maric at aristote.info
Fri May 23 01:39:29 EDT 2008


Le Friday 23 May 2008 04:28:22 thomas.karolski at googlemail.com, vous avez 
écrit :
> Hi,
> I would like to create a Decorator metaclass, which automatically
> turns a class which inherits from the "Decorator" type into a
> decorator.
> A decorator in this case, is simply a class which has all of its
> decorator implementation inside a decorator() method. Every other
> attribute access is being proxied to decorator().getParent().
>
> ...
>
> -------------------------------------------------------
> Unfortunately this does not work. The newly defined __init__ method
> inside __new__, does a call to impl(*args, **dargs). However, since
> the HBar.__init__ calls the Decorator.__init__ method, but the
> HBar.__init__ method no longer resides inside HBar, but rather inside
> HBarImpl (which is no longer a subtype of Decorator), the compiler
> complains that Decorator.__init__ is not being called with a Decorator
> instance as its first argument (which is true).
> I tried changing the definition of impl inside __new__ to have
> Decorator as one of its bases, but then for some reason impl(*args,
> **dargs) asks for 4 arguments (just like __new__) and I have no clue
> as to why that happens.
>
> Any help on this?
>

The problem with kind of design is that you must break the rules of class 
inheritance, and it seems like a strange idea to implement decorators by 
inheritance.

Of course you could do all sort of magic with python, but what is your goal ?
In your example, should the implementation types inherit from each other ?
In that case, do you want to preserve the same semantic for __init__ as in 
standard python class (this could be a hard job) ?

This quick fix seems to work with your example, but add extra magic to 
automatically call the super __init__ of the parent implementation, this 
could be a bad idea, use with caution ! (I still think it's a bad design, 
using composition and proxy classes is much more simple and clear)

class DecoratorType(type):
        def __new__(cls, name, bases, dct):

                # create a new class which will store all of the 
implementation
                parent_impl_type = bases[0] is object and object \
                                                       or bases[0]._impl_type
                impl = type('%sImpl'%name,(parent_impl_type,),dict(dct))
                dectype = type.__new__(cls, name, bases, {'_impl_type' : 
impl })

                # update the old class to implement this implementation
                def __init__(self, *args, **dargs):
                        print args, dargs
                        new_impl = impl(*args, **dargs)
                        super(dectype._impl_type, new_impl).__init__(*args,
                                                                     **dargs)
                        object.__setattr__(self, '_impl', new_impl)
                def decorator(self):
                        return object.__getattribute__(self,'_impl')
                def __getattribute__(self, attr):
                        if attr=="decorator":
                                return 
object.__getattribute__(self,'decorator')
                        return getattr(object.__getattribute__(
                                            self, 'decorator')(), attr)
                dectype.__init__ = __init__
                dectype.decorator = decorator
                dectype.__getattribute__ = __getattribute__

                return dectype

class Decorator(object):

        __metaclass__ = DecoratorType

class HBar(Decorator):
        def __init__(self, number):
                print 'hb:', number
                self._number = number
        def inc(self):
                self._number += 1
        def p(self):
                print self._number

class HBar2(HBar) :
        def __init__(self, number):
                print 'hb2:', number
                self._hb2 = number
        def inc2(self):
                self._hb2 += 1
        def p2(self):
                print self._hb2


hbar = HBar(10)
for each in dir(hbar.decorator()):
        print each

hbar.decorator().p()
hbar.decorator().inc()
hbar.decorator().p()

hb2 = HBar2(5)
hb2.p()
hb2.p2()
hb2.inc()
hb2.p()
hb2.p2()
hb2.inc2()
hb2.p()
hb2.p2()



-- 
_____________

Maric Michaud
_____________



More information about the Python-list mailing list