Here is the motivation for the script. It is very common to have a class
B with some __init__ method which is extended in a derived class C:
class B(object):
def __init__(self,*args,**kw):
print "This is B.__init__"
class C(B):
def __init__(self,*args,**kw):
B.__init__(self,*args,**kw)
print "This is C.__init__"
Notice that the B.__init__ method has to be called explicitly. In general
this is fine, but in some cases it would be handy to have B.__init__ called
automatically. To perform this, I used metaclasses:
-------------- begin MetaInit.py -----------------------
class MetaInit(type):
"""If cls inherits from bases, and cls.__metaclass__==MetaInit, then
cls.__init__ is extended to call super(cls).__init__ ."""
def __init__(cls,name,bases,dict):
child_init=cls.__init__ #makes a copy ?
def double_init(self,*args,**kw):
"Both the super and the child __init__ are called"
super(cls,self).__init__(self,*args,**kw)
child_init(self,*args,**kw)
setattr(cls,'__init__',double_init)
class Autoinit(object):
"""Autoinit should be thought as a mixin class. Classes derived from
Autoinit automagically call their super __init__ methods according to
the MRO. The magic works because Autoinit-derived classes inherit the
Autoinit metaclass MetaInit. Notice that Autounit itself inherit its
__init__ method from object, which does nothing."""
__metaclass__ = MetaInit
class B(Autoinit):
def __init__(self,*args,**kw):
print "This is B.__init__"
class C(Autoinit):
def __init__(self,*args,**kw):
print "This is C.__init__"
class D(B,C):
def __init__(self,*args,**kw):
print "This is D.__init__"
d=D()
------- end MetaInit.py -------
The output of this program is
This is C.__init__
This is B.__init__
This is D.__init__
i.e. what I want. Nevertheless, I wonder if there are cases where this script
could break down, then I ask the Python gurus for comments and feedback.
TIA,
--
Michele Simionato - Dept. of Physics and Astronomy
210 Allen Hall Pittsburgh PA 15260 U.S.A.
Phone: 001-412-624-9041 Fax: 001-412-624-9163
Home-page: http://www.phyast.pitt.edu/~micheles/
I think this example is cool enough that I saved it. But I still have
to wonder about this, much as I do about most metaclass examples,
whether one really -needs- metaclasses to do this. Of even if there is
anything clearer, easier to maintain, or any other advantage, about this
style.
Unfortunately, most of the posts discussing metaclasses still seem to be
at the level of "I'm trying to think of something to do with
metaclasses" (I've posted some like this too, I don't mean it as a
criticism).
The way I would generally approach this issue is with something like:
>>> class Super:
... def __init__(self):
... print "...in Super"
... self.init()
...
>>> class Child(Super):
... def init(self):
... print "...in Child"
...
>>> c = Child()
...in Super
...in Child
You could make this better, of course: maybe check for the '.init()'
method before calling it; or raise NotImplementedError specifically;
pass along some arguments to '.init()'; look through
'self.__class__.__bases__' for methods to call; and other stuff. Is
there anything that the metaclass approach gets you that a variant on
this one does not?
Yours, Lulu...
--
mertz@ | The specter of free information is haunting the `Net! All the
gnosis | powers of IP- and crypto-tyranny have entered into an unholy
.cx | alliance...ideas have nothing to lose but their chains. Unite
| against "intellectual property" and anti-privacy regimes!
-------------------------------------------------------------------------
> But I still have to wonder about this, much as I do about most metaclass
> examples, whether one really -needs- metaclasses to do this. Of even if
> there is anything clearer, easier to maintain, or any other advantage, about
> this style.
I thought exactly this for some time, this is the reason why I never took
in consideration the metaclasses approach until now. I was quite skeptical
about its advantages. But now I am reading "Putting Metaclasses to Work" and
I have got the feeling that there is something more in metaclasses.
The magic is somewhat in the fact that metaclasses can be inherited and used
in mixins as in the simple example I wrote. But still I haven't found a very
compelling example for the use of metaclasses, nor I fully grasp the
concept and its implications (yet). Nevertheless, my attitude is changing.
Until yesterday I was very skeptical about Alex Martelli's claims on the
power of metaclasses, today I have the impression that there is some meat in
and not only the smoke. But still I haven't seen a compelling example
in comp.lang.python (at least, one that I could understand ;-)
Michele
Disclaimer: I, too, haven't been able to use metaclasses practically
myself - mainly just played with how they work, and was amazed at their
versatility (and the things you can do _without_ them).
But, I can envision one immensly practical application -
object-relational mappers. Say you have a metaclass, DBSerializable.
This metaclass takes the classes attributes, which are supposed to hold
sentinel objects representing data types, and generates a class with all
the database serialization code automatically. So you can do:
class Client:
__metaclass__ = DBSerializable
id = DB_Int
name = DB_Str(50)
sales_rep_id = DB_Int(link=SalesRep.id)
and the metaclass wrapps everything in properties that automatically
handle database stuff, making everything smooth and transparent. Also,
maybe another metaclass makes the object model network-transparent, etc.
Hmm, I wonder if something like MiddleKit will ever adopt such an
idea...
-Mike
>i.e. what I want. Nevertheless, I wonder if there are cases where this script
>could break down, then I ask the Python gurus for comments and feedback.
It may break if any of the Autoinit classes do not have an __init__ method:
class B(Autoinit):
def __init__(self,*args,**kw):
print "This is B.__init__"
class C(B):
pass
class D(C):
def __init__(self,*args,**kw):
print "This is D.__init__"
d=D()
prints:
This is B.__init__
This is B.__init__
This is D.__init__
This is because cls.__init__ will return the nearest base class's __init__ if
cls does not itself define __init__. Your meta __init__ should probably look
like:
def __init__(cls,name,bases,dict):
child_init=cls.__dict__.get('__init__')
def double_init(self,*args,**kw):
"Both the super and the child __init__ are called"
super(cls,self).__init__(self,*args,**kw)
if child_init:
child_init(self,*args,**kw)
setattr(cls,'__init__',double_init)
---
Greg Chapman