On May 22, 10:28 pm, [EMAIL PROTECTED] wrote: > 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:
You got deep stuff going on there, chief, and some of it's wrong. I'll try to point it out. > ------------------------------------------------------- > from new import classobj > > class DecoratorType(type): > def __new__(cls, name, bases, dct): > dct2 = {} > for key, val in dct.iteritems(): > dct2[key] = val First of all, you can just do dct2 = dct.copy(). Second, since you never use dct again, even copying it is unnecessary. > # create a new class which will store all of the > implementation > impl = classobj('%sImpl'%name,(),dct2) classobj creates an old-style class, and I'm pretty sure you don't want to do that. To create a new-style class, use type: impl = type('%sImpl'%name,(),dct) > # update the old class to implement this implementation > def __init__(self, *args, **dargs): > object.__setattr__(self, '__impl', impl(*args, > **dargs)) As your code stands now, object.__setattr__ isn't necessary here; just using self.__impl = impl(*args,**dargs) should work fine. I'm guessing you intend to override __setattr__ later? If you do use object.__setattr__, I suggest that you might want to call the superclass's __setattr__ instead of object's. I imagine in this case the superclass will rarely want to override __setattr__ itself, but in general it's a good idea. In this particular circumstance, we don't yet have the class object (it doesn't come till after calling type.__new__) but we do have the parent class. So you might consider changing the definition of __init__ to this: basecls = bases[0] if bases else object def __init__(self, *args, **dargs): basecls.__setattr__(self, '__impl', impl(*args, **dargs)) > def decorator(self): > return object.__getattribute__(self,'__impl') Again, consider changing it to def decorator(self): return basecls.__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 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. However, I could see it being desirable for some cases where you're trying to be as transparent as possible, and indeed it looks as if that's your goal here. > 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. 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. However, this can cause problems if a user wants to define their own base class with an __init__ that does something (either by using the __metaclass__ attribute, or by subclassing a Decorator subclass). In that case, you will have to make arrangements to pass the decorator object to the superclass instead of the decorated. This can be pretty hairy, and it beyond the scope of this reply. To do it completely transparently, your decorated class will probably have to maintain a reference to its decorator, and will also have to derive from a base class that delegates any method calls to the superclass of the decorator. (Phew.) That won't be as easy as it sounds. > 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. I believe it's happening because you mixed old-style and new-style classes. But it's not the right solution anyway. > Any help on this? Probably the best piece of advice is "Don't try to use Decorator pattern". :) Seriously, you might want to see what other people have done in similar cases. This stuff is tricky to get right, so maybe you should shamelessly ride the coattails of someone who already ran into all the issues. One example I can think of is the ZODB Persistent class (it's a proxy class, so some of the same issues are involved). Perhaps searching Python cookbook for some proxy or decorator class recipes will give you ideas. Carl Banks -- http://mail.python.org/mailman/listinfo/python-list