Calling __init__ for all mixins

M

Martin Maney

I've been to the cookbook (both forms, since they're sometimes usefully
different), but that gave rise to more questions than answers. Heck,
let me pull some of them up while I'm at it.

Martelli gives the recipie (5.3 on paper) "Calling a Superclass
__init__ Method if it Exists" where he seems to say that this:

class NewStyleOnly(A, B, C):
def __init__(self):
super(NewStyleOnly, self).__init__()

is the New Class Speak equivalent of

class Classic(A, B, C):
def __init__(self):
for base in self.__class__.__bases__:
if hasattr(base, '__init__'):
base.__init__(self)

but when I tried a simple test case (base classes just print a tracer
when called), Martelli's NewStyleOnly only invoked A.__init__, as it
was the first base class in the base list as written with an __init__
implemented. These seem to me to be very different.

In the completely different category of things that make you scratch
your head, online recipie 146462 suggests code that seems to be a
candidate for best gratuitous use of 'continue'. Or is there some
subtle reason to prefer

for x in something:
if somecondition:
do_one_thing()
else:
continue
do_the_other_thing()

rather than the simpler and clearer

for x in something:
if somecondition:
do_one_thing()
do_the_other_thing()

or maybe even push the condition up into a comprehension in the for
statement. Yeah, it sometimes makes for long lines, but it also makes
it perfectly clear that you aren't doing *anything* with the filtered
elements. One thing I always thought C got stunningly right was its
for construct. How better to summarize a loop than to give its initial
conditions, loop invariant test, and fixup for next code all together
inone place? Well, given that you don't have lists and manage to make
the vast majority of loops be simple iterations over lists... :)

Yes, in retrospect maybe it would have been easier to have signed up
for ASPN and pointed this out in a comment, but I prefer not to
contribute to speedbumped sites like that both on principle, and as an
expression of essential laziness, and because there's a better audience
here anyway. Wait, that's three reasons...


So the reason I was turning over these stones was that I'm working on a
subsystem where I want to compose working classes using multiple mixin
base classes to provide different implementations for separate parts of
the interface. So far this is working quite nicely (1), but the
business of calling all the mixins' __init__ functions is a bit of a
nuisance. Unfortunately, the solutions I've found (aside from
Martelli's NewStyleOnly which I'm sure was only accidentally implied to
be a solution to this problem) all seem little is any better than the
simple and obvious method of explicitly invoking each one. I'm not
certain that I won't need to use that manual approach in the end, since
the current draft has a couple of arguments to one mixin, and it's not
clear I can eliminate them, but I would still be interested in any
suggestions for nicer solutions to the MI __init__ problem.


(1) the one nasty bit where a third mixin seems to want to provide a
few elements of what are otherwise two different mixin categories can
probably be subdued, if only by introducing the type of indirection
sometimes called "Strategy", though it's been a standard trick since
decades before design patterns were ever heard of.
 
S

Shalabh Chaturvedi

Martin said:
I've been to the cookbook (both forms, since they're sometimes usefully
different), but that gave rise to more questions than answers. Heck,
let me pull some of them up while I'm at it.

Martelli gives the recipie (5.3 on paper) "Calling a Superclass
__init__ Method if it Exists" where he seems to say that this:

class NewStyleOnly(A, B, C):
def __init__(self):
super(NewStyleOnly, self).__init__()

is the New Class Speak equivalent of

class Classic(A, B, C):
def __init__(self):
for base in self.__class__.__bases__:
if hasattr(base, '__init__'):
base.__init__(self)

but when I tried a simple test case (base classes just print a tracer
when called), Martelli's NewStyleOnly only invoked A.__init__, as it
was the first base class in the base list as written with an __init__
implemented. These seem to me to be very different.

So the reason I was turning over these stones was that I'm working on a
subsystem where I want to compose working classes using multiple mixin
base classes to provide different implementations for separate parts of
the interface. So far this is working quite nicely (1), but the
business of calling all the mixins' __init__ functions is a bit of a
nuisance. Unfortunately, the solutions I've found (aside from
Martelli's NewStyleOnly which I'm sure was only accidentally implied to
be a solution to this problem) all seem little is any better than the
simple and obvious method of explicitly invoking each one. I'm not
certain that I won't need to use that manual approach in the end, since
the current draft has a couple of arguments to one mixin, and it's not
clear I can eliminate them, but I would still be interested in any
suggestions for nicer solutions to the MI __init__ problem.

The new-style super mechanism might indeed solve your problem. In the
snippet that you mention, super(NewStyleOnly, self).__init__() would
only call A.__init__(). But A.__init__ should itself have a super call
of the form:

def __init__(self):
super(A, self).__init__() # A's super call

Now *that* will call B.__init__(), which should itself have a super call
of the form - you guessed it:

def __init__(self):
super(B, self).__init__()

Which will call C.__init__(). And so on. Also note that super() will
work correctly in the case you had a class:

class AnotherClass(A, C):
def __init__(self):
super(NewStyleOnly, self).__init__()

In this case, A's super call would call C.__init__(). Nice, huh?

This is the cooperative super call technique explained in the following
places:

http://www.python.org/2.2.3/descrintro.html#cooperation
http://www.cafepy.com/articles/python_attributes_and_methods/ch02.html

Any class wanting to participate in this technique must be new-style.

Cheers,
Shalabh
 
D

David Bolen

Martin Maney said:
I've been to the cookbook (both forms, since they're sometimes usefully
different), but that gave rise to more questions than answers. Heck,
let me pull some of them up while I'm at it.

Martelli gives the recipie (5.3 on paper) "Calling a Superclass
__init__ Method if it Exists" where he seems to say that this:

class NewStyleOnly(A, B, C):
def __init__(self):
super(NewStyleOnly, self).__init__()

is the New Class Speak equivalent of

class Classic(A, B, C):
def __init__(self):
for base in self.__class__.__bases__:
if hasattr(base, '__init__'):
base.__init__(self)

but when I tried a simple test case (base classes just print a tracer
when called), Martelli's NewStyleOnly only invoked A.__init__, as it
was the first base class in the base list as written with an __init__
implemented. These seem to me to be very different.

Can you show your test code. I've found that you may need to ensure
all of your classes are cooperating in their use of super(), as in the
following:

mro.py:

class A(object):

def __init__(self):
print "A init"
super(A,self).__init__()

class B(object):

def __init__(self):
print "B init"
super(B,self).__init__()

class C(object):

def __init__(self):
print "C init"
super(C,self).__init__()


class Mix(A,B,C):

def __init__(self):
print "Mix init"
super(Mix,self).__init__()


Python 2.2.3 (#42, May 30 2003, 18:12:08) [MSC 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information. Mix init
A init
B init
C init

The new style MRO approach will only work if all of your objects are
new style classes and they all participate in the cooperative process
through the use of super(). Also, the precise MRO used has changed
between 2.2 and 2.3 but it shouldn't affect the above syntax.

I do think it's easy to find yourself skipping the call to object's
__init__ in classes since it doesn't necessarily do anything, but it's
those other super calls that provide the chance to iterate through the
inheritance hierarchy based on the initial call from the ultimate
subclass.

Guido has some info on the cooperative use of super in his 2.2
unification draft available at http://www.python.org/2.2.3/descrintro.html
and more info on the MRO change is at http://www.python.org/2.3/mro.html.

-- David
 
M

Martin Maney

Shalabh Chaturvedi said:
The new-style super mechanism might indeed solve your problem. In the
snippet that you mention, super(NewStyleOnly, self).__init__() would
only call A.__init__(). But A.__init__ should itself have a super call
of the form:
def __init__(self):
super(A, self).__init__() # A's super call
Now *that* will call B.__init__(), which should itself have a super call

I will be dipped in shit. So let me see... super(this_here_class,
self) can and will resolve to a class that is not in any sane meaning
of the word a super class of this_here_class, but rather a co-base of
it? This is either brilliant or pure crack. I'll have to ponder it
for a good long while.

Brilliant. Crack. Brilliant. Crack. ... Luminiferous Aether!

Was this chosen specifically to confuse anyone familiar with OO
terminology as used in other languages? said:
Any class wanting to participate in this technique must be new-style.

Which brings me back to the original motivation: any nice tricks for
doing this without NewSpeek classes? Maybe in a year or so I can get
all the production machines upgraded; for now, I have to write to the
subset that works across 2.1, 2.2, and, mostly just as a nod to that
future so far, 2.3 (I frankly haven't the time to do much worrying
about 2.4 yet, and don't expect to any time soon. It would be nice to
have only one or two machines to take care of and enough free time to
keep up to date with every release.)
 
M

Michael Hudson

Martin Maney said:
I will be dipped in shit. So let me see... super(this_here_class,
self) can and will resolve to a class that is not in any sane meaning
of the word a super class of this_here_class, but rather a co-base of
it?

No. super(this_here_class, self) returns, well, a super() object that
when you look for an attribute on it acts a bit like `self.__class__'
but only looks in classes later than `this_here_class' in the MRO.

Don't worry too much if that didn't make sense, but it's probably a
good idea to get the thought that super() returns a class out of your
head.
This is either brilliant or pure crack. I'll have to ponder it for
a good long while.

It's brilliant :)

Cheers,
mwh

--
> Well, as an American citizen I hope that the EU tells the MPAA
> and RIAA to shove it where the Sun don't shine.
Actually they already did. Only first they bent over and dropped
their trousers. -- Shmuel (Seymour J.) Metz & Toni Lassila, asr
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top