34

I'm just diving into some more advanced python subjects (well, advanced to me at least). I am now reading about multiple inheritance and how you can use super(). I more or less understand the way the super function is used, but (1) What's wrong with just doing it like this?:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        First.__init__(self)
        Second.__init__(self)
        print "that's it"

On to super(), Andrew Kuchlings paper on Python Warts says:

usage of super() will also be correct when the Derived class inherits from multiple base classes and some or all of them have init methods

So I rewrote the example above as follows:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__(self)
        print "that's it"

This however, only runs the first init it can find, which is in First. (2) Can super() be used to run both the init's from First and Second, and if so, how? Running super(Third, self).__init__(self) twice just runs First.init() twice..

To add some more confusion. What if the inherited classes' init() functions take different arguments. For example, what if I had something like this:

class First(object):
    def __init__(self, x):
        print "first"

class Second(object):
    def __init__(self, y, z):
        print "second"

class Third(First, Second):
    def __init__(self, x, y, z):
        First.__init__(self, x)
        Second.__init__(self, y, z)
        print "that's it"

(3) How would I be able to supply the relevant arguments to the different inherited classes init functions using super()?

All tips are welcome!

ps. Since I have several questions I made them bold and numbered them..

kramer65
  • 50,427
  • 120
  • 308
  • 488
  • `super(Class, self)` returns an object, so you shouldn't call it with `.__init__(self)`, but just `.__init__()`. You can see this in the answers, but your original first code returns exception `TypeError: __init__() takes exactly 1 argument (2 given)`. – Tomasz Gandor May 28 '14 at 05:47

2 Answers2

13

For question 2, you need to call super in each class:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print "first"

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

For question 3, that can't be done, your method needs to have the same signature. But you could just ignore some parameters in the parent clases or use keywords arguments.

Guillaume
  • 10,463
  • 1
  • 33
  • 47
  • 2
    So you mean the use of super() is dependent on the use of super() in the inherited classes? What if I do not control the inherited classes? Would I then simply do First.__init__(self) and Second.__init__(self) like in my first example? – kramer65 May 31 '13 at 11:08
  • 1
    Yes, "super" is a cooperative process, all classes needs to use the mechanism or it just doesn't works. You can find a bit more info here: http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ And yes, if a class in the hierarchy doesn't use super, I think you have to use the "old style" way :) – Guillaume May 31 '13 at 11:13
  • 2
    Thanks for that. Just one last question; why is super() added to the language anyway? What's the advantage of it over the "old style"? – kramer65 May 31 '13 at 13:37
  • 1
    I see two advantages: you don't have to explicitly reference the parent class and in case of multiple inheritance you don't have to call multiple parent constructors... also in python3, you only have to call super().something() instead of super(Class, self).something() – Guillaume May 31 '13 at 13:46
  • 1
    You say: "in case of multiple inheritance you don't have to call multiple parent constructors". I don't understand that though, because if I call super(CurrentClass, self).__init__() (as I do in my second piece of code) it only runs the init function of the first inherited class instead of the init of all inherited classes. So that means that super() cannot be used when doing multiple inheritance.. right? – kramer65 May 31 '13 at 18:01
  • 1
    No, as I'm saying in my post, if all your parents classes have call to super too, you only need one call (try to run my modified example, there's only one call to super in the child, but both the parent constructors are called) – Guillaume May 31 '13 at 18:05
  • 1
    Oh, yes. Excuse me. I just had dinner and am a bit slow. It's a lot of information I had to digest today.. :) So to conclude. I can only use super() if the inherited classes also have super() statements and the init functions of the inherited classes don't need custom arguments. Correct? – kramer65 Jun 01 '13 at 11:25
  • You technically don't need the super in seconds init, unless there was another object Third was inheriting from but it doesn't hurt. – radtek Oct 01 '14 at 14:42
  • `super` goes against Python's own `explicit is better than implicit`, it depends on MRO (cooperation in parents!) and memorizes the parent class objects in first init call which cause other problems if reimporting modules! Why is it added to language, good question, looks like a half-arsed ugly hack. – dashesy Jan 28 '15 at 21:27
  • @dashesy: Because explicit is better than implicit. Overriding a method normally means that you are taking full responsibility for all of its functionality. Now, what is the functionality of `__init__()`? Initializing the object. You don't know how to initialize the *entire* object, though, because multiple inheritance might give you superclasses you don't know about. So you have to call `super()`. As for "memorizes the parent class objects in first init call", I have no idea what you're talking about. – Kevin Feb 21 '15 at 15:47
3

1) There is nothing wrong in doing like what u have done in 1, if u want to use the attributes from base class then u have to call the base class init() or even if u r using methods from base class that uses the attributes of its own class then u have to call baseclass init()

2) You cant use super to run both the init's from First and Second because python uses MRO (Method resolution order)

see the following code this is diamond hierarchy

class A(object): 
    def __init__(self):
        self.a = 'a'
        print self.a

class B(A):
    def __init__(self):
        self.b = 'b'
        print self.b

class C(A):
    def __init__(self):
        self.c = 'c'
        print self.c

class D(B,C):
    def __init__(self):
        self.d = 'd'
        print self.d
        super(D, self).__init__()

d = D()
print D.mro()

It prints:

d
b
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>]

MRO of python would be D,B,C,A

if B does't have init method it goes for C.

3) You can't do it all methods need to have same signature.

radtek
  • 34,210
  • 11
  • 144
  • 111
Vb407
  • 1,577
  • 2
  • 15
  • 27