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:
We can directly subclass built-in classes, like
dict
,list
,tuple
, etc.The
super
function handles tracking down this class's superclasses and calling functions in them appropriately.
Related Topics
Python Script as Linux Service/Daemon
Call to Operating System to Open Url
Can Python Select What Network Adapter When Opening a Socket
Is There a Python Equivalent to Java'S Awt Robot Class
How to Use Export With Python on Linux
After Anaconda Installation, Conda Command Fails With "Importerror: No Module Named Conda.Cli"
Force Another Program'S Standard Output to Be Unbuffered Using Python
Store Large Data or a Service Connection Per Flask Session
Integer Division by Negative Number
Why Do Some Regex Engines Match .* Twice in a Single Input String
Faster Version of 'Pygame.Event.Get()'. Why Are Events Being Missed and Why Are the Events Delayed
Get Difference Between Two Lists
Fatal Error: Python.H: No Such File or Directory
How to Sort a List/Tuple of Lists/Tuples by the Element At a Given Index
Check If Multiple Strings Exist in Another String
How to Sort a List of Dictionaries by a Value of the Dictionary