What Does 'Super' Do in Python? - Difference Between Super()._Init_() and Explicit Superclass _Init_()

Understanding Python super() with __init__() methods

super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

Note that the syntax changed in Python 3.0: you can just say super().__init__() instead of super(ChildB, self).__init__() which IMO is quite a bit nicer. The standard docs also refer to a guide to using super() which is quite explanatory.

How does Python's super() work with multiple inheritance?

This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order (including two earlier attempts).

In your example, Third() will call First.__init__. Python looks for each attribute in the class's parents as they are listed left to right. In this case, we are looking for __init__. So, if you define

class Third(First, Second):
...

Python will start by looking at First, and, if First doesn't have the attribute, then it will look at Second.

This situation becomes more complex when inheritance starts crossing paths (for example if First inherited from Second). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.

So, for instance, if you had:

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

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

class Third(First):
def __init__(self):
print "third"

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

the MRO would be [Fourth, Second, Third, First].

By the way: if Python cannot find a coherent method resolution order, it'll raise an exception, instead of falling back to behavior which might surprise the user.

Example of an ambiguous MRO:

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

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

class Third(First, Second):
def __init__(self):
print "third"

Should Third's MRO be [First, Second] or [Second, First]? There's no obvious expectation, and Python will raise an error:

TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First

Why do the examples above lack super() calls? The point of the examples is to show how the MRO is constructed. They are not intended to print "first\nsecond\third" or whatever. You can – and should, of course, play around with the example, add super() calls, see what happens, and gain a deeper understanding of Python's inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)

Should __init__() call the parent class's __init__()?

In Python, calling the super-class' __init__ is optional. If you call it, it is then also optional whether to use the super identifier, or whether to explicitly name the super class:

object.__init__(self)

In case of object, calling the super method is not strictly necessary, since the super method is empty. Same for __del__.

On the other hand, for __new__, you should indeed call the super method, and use its return as the newly-created object - unless you explicitly want to return something different.

How is `tuple.__init__` different from `super().__init__` in a subclass of tuple?

If you call tuple.__init__ it returns object.__init__ because tuple has no custom __init__ method and only inherits it from object. The first argument for object.__init__ is self and what object.__init__ does is nothing. So when you pass in contents it's interpreted as self and doesn't throw an Exception. However it probably doesn't do what you think it does because tuple.__new__ is responsible for setting up a new tuple instance.

If you use super().__init__ it also resolves to object.__init__ but it already binds the current "self" as first argument. So when you pass contents to this function it's interpreted as additional argument which doesn't exist for object.__init__ and therefore throws that Error.

Python inheritance: TypeError: object.__init__() takes no parameters

You are calling the wrong class name in your super() call:

class SimpleHelloWorld(IRCReplyModule):

def __init__(self):
#super(IRCReplyModule,self).__init__('hello world')
super(SimpleHelloWorld,self).__init__('hello world')

Essentially what you are resolving to is the __init__ of the object base class which takes no params.

Its a bit redundant, I know, to have to specify the class that you are already inside of, which is why in python3 you can just do: super().__init__()

Why aren't superclass __init__ methods automatically invoked?

The crucial distinction between Python's __init__ and those other languages constructors is that __init__ is not a constructor: it's an initializer (the actual constructor (if any, but, see later;-) is __new__ and works completely differently again). While constructing all superclasses (and, no doubt, doing so "before" you continue constructing downwards) is obviously part of saying you're constructing a subclass's instance, that is clearly not the case for initializing, since there are many use cases in which superclasses' initialization needs to be skipped, altered, controlled -- happening, if at all, "in the middle" of the subclass initialization, and so forth.

Basically, super-class delegation of the initializer is not automatic in Python for exactly the same reasons such delegation is also not automatic for any other methods -- and note that those "other languages" don't do automatic super-class delegation for any other method either... just for the constructor (and if applicable, destructor), which, as I mentioned, is not what Python's __init__ is. (Behavior of __new__ is also quite peculiar, though really not directly related to your question, since __new__ is such a peculiar constructor that it doesn't actually necessarily need to construct anything -- could perfectly well return an existing instance, or even a non-instance... clearly Python offers you a lot more control of the mechanics than the "other languages" you have in mind, which also includes having no automatic delegation in __new__ itself!-).

Inheritance and Overriding __init__ in python

The book is a bit dated with respect to subclass-superclass calling. It's also a little dated with respect to subclassing built-in classes.

It looks like this nowadays:

class FileInfo(dict):
"""store file metadata"""
def __init__(self, filename=None):
super(FileInfo, self).__init__()
self["name"] = filename

Note the following:

  1. We can directly subclass built-in classes, like dict, list, tuple, etc.

  2. The super function handles tracking down this class's superclasses and calling functions in them appropriately.



Related Topics



Leave a reply



Submit