Decorator metaclass
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(). Here's my attempt: --- from new import classobj class DecoratorType(type): def __new__(cls, name, bases, dct): dct2 = {} for key, val in dct.iteritems(): dct2[key] = val # create a new class which will store all of the implementation impl = classobj('%sImpl'%name,(),dct2) # update the old class to implement this implementation def __init__(self, *args, **dargs): object.__setattr__(self, '__impl', impl(*args, **dargs)) 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') ().getParent(), attr) dct = {} dct['__init__'] = __init__ dct['decorator'] = decorator dct['__getattribute__'] = __getattribute__ return type.__new__(cls, name, bases, dct) class Decorator(object): __metaclass__ = DecoratorType class HBar(Decorator): def __init__(self, number): Decorator.__init__(self) self._number = number def inc(self): self._number += 1 def p(self): print self._number hbar = HBar(10) for each in dir(hbar.decorator()): print each hbar.decorator().p() hbar.decorator().inc() hbar.decorator().p() --- 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? Regards, Thomas K. -- http://mail.python.org/mailman/listinfo/python-list
Re: Decorator metaclass
Thanks for pointing out all those mistakes. I think I'm already starting to grasp all of the python magic going on in there. Parenthetical: I don't normally recommend this style, since it obscures the fact that you're using a custom metaclass to the user. That is something the user probably would benefit from knowing, if for no other reason than so they can make a mental note about where to look first if something goes wrong. I prefer to make the user use the __metaclass__ attribute. Really just personal preference I think. I'm not really a friend of declaring variables if there is a more "intuitive" way. class HBar(Decorator): def __init__(self, number): Decorator.__init__(self) Ok, at this point you have to ask yourself what you want to do, because the solution you choose will involve trade-offs. Yes, it was probably a bad example. I decided not to call the Decorator 's __init__ method in my new version (which I have posted as a reply to the reply of Maric Michaud). You will note that Decorator does not define __init__. In fact, object.__init__ will be called, which does nothing. If you think that all classes with DecoratorType as their metaclass will be a direct subclass of Decorator, you can get away with not calling Decorator.__init__ at all. Now, inside my new version, I have a class which inherits from both Decorator and Window, out of which the __init__ for Decorator is not called. Does this prove to be a problem? Probably the best piece of advice is "Don't try to use Decorator pattern". :) Well, I decided on the decorator pattern, because I want to be able to change the behavior of classes during run-time. I did not really find any other pattern which would let me do that. Regards, Thomas K. -- http://mail.python.org/mailman/listinfo/python-list
Re: Decorator metaclass
Turns out the first msg I sent did not reach the list, so I'll just post what I've achieved by now: -- class DecoratorDummy(object): pass class InheritedDecoratorType(type): def __new__(cls, name, bases, dct): # return if its a class which inherited from Decorator if Decorator in bases: return type.__new__(cls, name, bases, {}) # if its a class which did not directly inherit from Decorator, # then it inherited from a class which has been manipulated using the # Decorator class. # in that case we change the bases of the inheriting class. # We'll split the manipulated class into Decorator and its implementation for b in bases: if type(b) is InheritedDecoratorType: break newbases = [x for x in bases] # remove the manipulated base class newbases.remove(b) # and add the impl of the manipulated base class newbases.append(b._impl_type) # and add the Decorator class newbases.append(Decorator) # now we'll have to make sure the dict of the new class shows the original base class # (which has been removed) as the implementation class dct[b.__name__] = b._impl_type # since we have added a Decorator class, we ought to get rid of it # through the DecoratorType metaclass r = DecoratorType.__new__(cls, name, tuple(newbases), dct) return r class DecoratorType(type): def __new__(cls, name, bases, dct): # if the first class is DecoratorDummy, then we're handling the # Decorator class, which is not supposed to be modified if bases[0] is DecoratorDummy: return type.__new__(cls, name, bases, {}) # if one of the bases is the Decorator class b = [x for x in bases] if Decorator in b: # create a new impl type which inherits from every but the decorator class b.remove(Decorator) impl_type = type('%sImpl'%name, tuple(b), dict(dct)) # make the returned type no longer a DecoratorType, but rather a normal type # Types which inherit from a class which inherited from Decorator, will thus # *not* be put through this metaclass. #dectype = type.__new__(type, name, tuple(b), {'_impl_type' : impl_type }) dectype = type.__new__(InheritedDecoratorType, name, tuple(b), {'_impl_type' : impl_type }) # update the old class to implement this implementation def __init__(self, *args, **dargs): new_impl = impl_type(*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') # if we have a specified method inside the decorator().__decorate__ var, # then call decorator().attr(), otherwise proxy the call d = object.__getattribute__(self, 'decorator')() if attr in d.__decorate__: return getattr(d, attr) return getattr(d.getParent(), attr) dectype.__init__ = __init__ dectype.decorator = decorator dectype.__getattribute__ = __getattribute__ return dectype class Decorator(DecoratorDummy): __metaclass__ = DecoratorType class Window(object): def __init__(self, parent): print "Window::__init__(%s)"%self self._parent = parent def setParent(self, parent): self._parent = parent def getParent(self): return self._parent class Textarea(Window): def draw(self): print "Textarea::draw()" class HBar(Decorator, Window): __decorate__ = ["draw"] def __init__(self, parent): print "HBar::__init__(%s)"%self Window.__init__(self, parent=parent) self._progress = 0.0