Python 2.3.3 super() behaviour
David Fraser
davidf at sjsoft.com
Thu Apr 22 01:42:28 EDT 2004
Hi Nicolas
Another related idea:
Use template functions when you have enough control over the objects:
def A(base=object):
class A(base):
def __init__(self):
print "A"
super(A, self).__init__()
return A
def B(base=object):
class B(base):
def __init__(self):
print "B"
super(B, self).__init__()
return B
class C(B(A())):
def __init__(self):
print "C"
super(C, self).__init__()
C()
This is a bit different to multiple inheritance, but can have nice
effects...
David
Nicolas Lehuen wrote:
> Ah, so there *is* more than one way to do it, then ? ;)
>
> What the example shows is that super(...).__init__ does make one call to
> each superclass' __init__, provided that those superclasses play nicely and
> use super() themselves (as Peter wrote). If one of the superclasses does not
> use super(), the 'magical' iteration over parent methods is interrupted,
> hence the need to put those bad guys at the end of the superclasses list in
> the class declaration.
>
> But you're right, David, the simplest way to get what I want is to
> explicitely call the superclasses' __init__. However, this would mean that
> super() is not as useful as it seems. I'd rather find a way to *always* use
> super() than have special cases for certain inheritance trees. In other
> words, I'd rather have only one way to do it :).
>
> Regards,
> Nicolas
>
> "David Fraser" <davidf at sjsoft.com> a écrit dans le message de
> news:c65j4o$3me$1 at ctb-nnrp2.saix.net...
>
>>super is not going to be helpful here, so why not call the X.__init__
>>functions explicitly?
>>Since you *need* multiple __init__ calls from the multiply-inherited
>>class, super isn't going to work...
>>
>>Nicolas Lehuen wrote:
>>
>>>The only problem I have is that I want to build a multiple inheritance
>>>involving an object which does not cooperate, namely :
>>>
>>>class T(object):
>>> def __init__(self):
>>> super(T,self).__init__()
>>>
>>>class TL(list,object):
>>> def __init__(self)
>>> super(TL,self).__init__()
>>>
>>>In this case, T.__init__ is not called, because list.__init__ does not
>
> use
>
>>>super(). The only clean way to proceed is to change the inheritance
>
> order :
>
>>>TL(T,list). This way, both constructors are called.
>>>
>>>Here is another example which exhibits the behaviour :
>>>
>>>class A(object):
>>> def __init__(self):
>>> super(A,self).__init__()
>>> print 'A'
>>>
>>>class B(object):
>>> def __init__(self):
>>> print 'B'
>>>
>>>class C(B,A):
>>> def __init__(self):
>>> super(C,self).__init__()
>>> print 'C'
>>>
>>>class D(A,B):
>>> def __init__(self):
>>> super(D,self).__init__()
>>> print 'D'
>>>
>>>
>>>
>>>>>>C()
>>>
>>>B
>>>C
>>><__main__.C object at 0x008F3D70>
>>>
>>>>>>D()
>>>
>>>B
>>>A
>>>D
>>><__main__.D object at 0x008F39F0>
>>>
>>>The problem is that if you go further down the inheritance, the
>
> behaviour is
>
>>>even more complicated :
>>>
>>>class E(object):
>>> def __init__(self):
>>> super(E,self).__init__()
>>> print 'E'
>>>
>>>class F(C,E):
>>> def __init__(self):
>>> super(F,self).__init__()
>>> print 'F'
>>>
>>>class G(D,E):
>>> def __init__(self):
>>> super(G,self).__init__()
>>> print 'G'
>>>
>>>
>>>
>>>>>>F()
>>>
>>>B
>>>C
>>>F
>>><__main__.F object at 0x008F3D70>
>>>
>>>>>>G()
>>>
>>>B
>>>A
>>>D
>>>G
>>><__main__.G object at 0x008F3EF0>
>>>
>>>class H(E,C):
>>> def __init__(self):
>>> super(H,self).__init__()
>>> print 'H'
>>>
>>>class I(E,D):
>>> def __init__(self):
>>> super(I,self).__init__()
>>> print 'I'
>>>
>>>
>>>
>>>>>>H()
>>>
>>>B
>>>C
>>>E
>>>H
>>><__main__.H object at 0x008F3E30>
>>>
>>>>>>I()
>>>
>>>B
>>>A
>>>D
>>>E
>>>I
>>><__main__.I object at 0x008F3FD0>
>>>
>>>So the conclusion is : never do that :). Another more constructive
>>>conclusion would be : always put the most cooperative classes first in
>
> the
>
>>>inheritance declaration, provided that it doesn't interfere with your
>
> needs.
>
>>>A class which has an uncooperative ancestor is less cooperative than a
>
> class
>
>>>which has only cooperative ancestors.
>>>
>>>Regards,
>>>Nicolas
>>>
>>>"Nicolas Lehuen" <nicolas.lehuen at thecrmcompany.com> a écrit dans le
>
> message
>
>>>de news:40864674$0$24834$afc38c87 at news.easynet.fr...
>>>
>>>
>>>>OK, I get it now, thanks.
>>>>
>>>>super() method calls should only be used for method involved in
>>>>diamond-shaped inheritance. This is logical since in this case the base
>>>>classe (from which the diamond-shaped inheritance starts) defines the
>>>>interface of the method.
>>>>
>>>>This solves another question I was asking myself about super() : "how
>
> can
>
>>>it
>>>
>>>
>>>>work when the method signature differ between B and C ?". Answer : the
>>>>method signature should not change because polymorphic calls would be
>>>>greatly endangered. The base class defines the signature of the method
>>>
>>>which
>>>
>>>
>>>>must be followed by all its children, this way super() can work
>
> properly.
>
>>>>The base method signature is not enforced by Python, of course, but
>
> you'd
>
>>>>better respect it unless you get weird result in polymorphic calls.
>>>>
>>>>Regards,
>>>>Nicolas
>>>>
>>>>"Peter Otten" <__peter__ at web.de> a écrit dans le message de
>>>>news:c65fbo$1q4$05$1 at news.t-online.com...
>>>>
>>>>
>>>>>Nicolas Lehuen wrote:
>>>>>
>>>>>
>>>>>
>>>>>>Hi,
>>>>>>
>>>>>>I hope this is not a FAQ, but I have trouble understanding the
>>>
>>>behaviour
>>>
>>>
>>>>>>of the super() built-in function. I've read the excellent book 'Python
>>>>
>>>>in
>>>>
>>>>
>>>>>>a Nutshell' which explains this built-in function on pages 89-90.
>>>
>>>Based
>>>
>>>
>>>>on
>>>>
>>>>
>>>>>>the example on page 90, I wrote this test code :
>>>>>>
>>>>>>class A(object):
>>>>>> def test(self):
>>>>>> print 'A'
>>>>>>
>>>>>>class B(object):
>>>>>> def test(self):
>>>>>> print 'B'
>>>>>>
>>>>>>class C(A,B):
>>>>>> def test(self):
>>>>>> super(C,self).test()
>>>>>> print 'C'
>>>>>>
>>>>>>print C.__mro__
>>>>>>c=C()
>>>>>>c.test()
>>>>>>
>>>>>>The output is :
>>>>>>(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
>>>
>>><type
>>>
>>>
>>>>>>'object'>)
>>>>>>A
>>>>>>C
>>>>>>
>>>>>>Whereas I was expecting :
>>>>>>(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
>>>
>>><type
>>>
>>>
>>>>>>'object'>)
>>>>>>A
>>>>>>B
>>>>>>C
>>>>>>
>>>>>>Was I wrong to expect this (based on what I've read ?)
>>>>>
>>>>>As soon as a test() method without the super(...).test() is reached, no
>>>>>further test methods will be invoked. Only the first in the list of
>
> base
>
>>>>>classes will be invoked. If I'm getting it right you have to do
>>>
>>>something
>>>
>>>
>>>>>like:
>>>>>
>>>>>class Base(object):
>>>>> def test(self):
>>>>> print "base"
>>>>>
>>>>>class D1(Base):
>>>>> def test(self):
>>>>> super(D1, self).test()
>>>>> print "derived 1"
>>>>>
>>>>>class D2(Base):
>>>>> def test(self):
>>>>> super(D2, self).test()
>>>>> print "derived 2"
>>>>>
>>>>>class All(D1, D2):
>>>>> pass
>>>>>
>>>>>All().test()
>>>>>
>>>>>Here all cooperating methods have a super() call, and the base class
>>>
>>>acts
>>>
>>>
>>>>as
>>>>
>>>>
>>>>>a showstopper to prevent that Python tries to invoke the non-existent
>>>>>object.test().
>>>>>
>>>>>Peter
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>
>
More information about the Python-list
mailing list