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