473,396 Members | 2,033 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

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.
Jun 27 '08 #1
4 2444
Le Friday 23 May 2008 04:28:22 th*************@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
_____________
Jun 27 '08 #2
On May 22, 10:28 pm, thomas.karol...@googlemail.com 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
Jun 27 '08 #3
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.

Jun 27 '08 #4
On May 23, 11:42 am, Thomas Karolski <Thomas.Karol...@googlemail.com>
wrote:
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?
It sounds like it could create infinite recursion. If your decorated
class inherits from Decorator, it should also get the DecorateType
metaclass, which should recursively try to create its own decorated
class, ad infinitum. Unless I misunderstand what you meant.

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.

Ah.

Well it is possible to do that in Python. though there's probably not
an official design pattern for it (but then design patterns grew up
around less flexible languages, partially as a way to cope with their
lack of flexibility). Here are a couple things to think about:

If you'd like to change the behavior of individual instances of a
class, you can assign functions to individual instances which act just
like methods. (Warning: this does not work with operators.)

Here is an example:

class A(object):
def say_hello(self):
print "hello, world"

a = A()

def custom_hello():
print "hello, my name is Inigo Montoya"

a.say_hello = custom_hello
If you'd like to change the behavior of all instances of the class,
then you can assign a new method directly to the class after it was
created:

def new_default_hello(self):
print "hello, children"

A.say_hello = new_default_hello
Notice that you need to use self when assigning it to the class
object, and not to use self when assigning it to an instance of the
class.
Carl Banks
Jun 27 '08 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Carlos Ribeiro | last post by:
Hello all, While studying some of the templating systems available for Python, I've come up with an idea to implement templates using decorators. The following code snippet runs in Python 2.3; a...
11
by: glen.coates.bigworld | last post by:
I'm developing a library at the moment that involves many classes, some of which have "exposed" capabilities. I'm trying to design a nice interface for both exposing those capabilities, and...
4
by: c james | last post by:
I want to use the LRU decorator posted at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498110 on a class method. However, it complains about missing arguments. The missing argument is...
4
by: Wilbert Berendsen | last post by:
Hi, is it possible to manipulate class attributes from within a decorator while the class is being defined? I want to register methods with some additional values in a class attribute. But I...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.