293
votes

Consider this - a base class A, class B inheriting from A, class C inheriting from B. What is a generic way to call a parent class initialiser in an initialiser? If this still sounds too vague, here's some code.

class A(object):
    def __init__(self):
        print "Initialiser A was called"

class B(A):
    def __init__(self):
        super(B,self).__init__()
        print "Initialiser B was called"

class C(B):
    def __init__(self):
        super(C,self).__init__()
        print "Initialiser C was called"

c = C()

This is how I do it now. But it still seems a bit too non-generic - you still must pass a correct type by hand.

Now, I've tried using self.__class__ as a first argument to super(), but, obviously it doesn't work - if you put it in the initialiser for C - fair enough, B's initialiser gets called. If you do the same in B, "self" still points to an instance of C so you end up calling B's initialiser again (this ends in an infinite recursion).

There is no need to think about diamond inheritance for now, I am just interested in solving this specific problem.

3
"But it still seems a bit too non-generic - you still must pass a correct type by hand."?? You're passing the class name to super(). You never need to know the superclass or anything else. How is this non-generic? What problem does it create? Can you give an example where this breaks? - S.Lott
I am not ready to answer this question if full. But anyway, what if the class gets renamed? What if I want to write a decorator function to automate this kind of chaining? Now I see, that this is not possible, but at the time of asking the question I did not know that. - shylent
@shylent, if I understand your question, what your describing is possible. You can use super() to make parent references fully generic if you understand the mro. Google "python super is super", watch the video. - Jamie Marshall
@shylent actually, reading your question again I believe the infinite recursion you're experiencing is not what you think it is. I believe the mro order is making you call a constructor you're not intending to. I just dealt with this same thing myself not long ago. No matter what you pass to the first argument of super, super will call the first constructor in the mro, not the type you passed in. For instance if you call super(self.__class__, self) in the constructor of C, you will still call B's constructor because it is the first parent in the mro. - Jamie Marshall

3 Answers

170
votes

The way you are doing it is indeed the recommended one (for Python 2.x).

The issue of whether the class is passed explicitly to super is a matter of style rather than functionality. Passing the class to super fits in with Python's philosophy of "explicit is better than implicit".

213
votes

Python 3 includes an improved super() which allows use like this:

super().__init__(args)
28
votes

You can simply write :

class A(object):

    def __init__(self):
        print "Initialiser A was called"

class B(A):

    def __init__(self):
        A.__init__(self)
        # A.__init__(self,<parameters>) if you want to call with parameters
        print "Initialiser B was called"

class C(B):

    def __init__(self):
        # A.__init__(self) # if you want to call most super class...
        B.__init__(self)
        print "Initialiser C was called"