How to Invoke the Super Constructor in Python

How to invoke the super constructor in Python?

In line with the other answers, there are multiple ways to call super class methods (including the constructor), however in Python-3.x the process has been simplified:

Python-3.x

class A(object):
def __init__(self):
print("world")

class B(A):
def __init__(self):
print("hello")
super().__init__()

Python-2.x

In python 2.x, you have to call the slightly more verbose version super(<containing classname>, self), which is equivalent to super()as per the docs.

class A(object):
def __init__(self):
print "world"

class B(A):
def __init__(self):
print "hello"
super(B, self).__init__()

Calling superclass constructors in python with different arguments

Other answers suggested adding self to the first parameter.

But usually invocations of __init__ in parent classes are made by super.

Consider this example:

class A(object):
def __init__(self, x):
print('__init__ is called in A')
self.x = x

class B(object):
def __init__(self, *args, **kwargs):
print('__init__ is called in B')
super(B, self).__init__(*args, **kwargs)

class AB(B, A):
def __init__(self, *args, **kwargs):
print('__init__ is called in AB')
super(AB, self).__init__(*args, **kwargs)

AB class contains an order in which constructors and initializators should be called:

>>> AB.__mro__
(<class '__main__.AB'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)

See, that first AB's __init__ is invoked, then B's, then A's, and then object's.

Let's check:

>>> ab = AB(1)
__init__ is called in AB
__init__ is called in B
__init__ is called in A

But these calls through this chain are made by super. When we type super(AB, self), it means: find then next class after AB in __mro__ chain of self.

Then we should invoke super in B, looking for the next class in the chain after B: super(B, self).

It's important to use super and not call manually A.__init__(self,...), etc., as it may lead to problems later. Read this for more info.

So, if you stick with super, then there is a problem. __init__ methods in your classes expect different parameters. And you can't know for sure the order in which super will be invoking methods in these classes. The order is determined by C3 algorithm at the time of class creation. In subclasses another classes may get in-between of the call chain. So you can't have different parameters in __init__, as in this case you will have always to consider all inheritance chain to understand how __init__ methods will be called.

For example, consider adding C(A) and D(B) classes and CD subclass of them. Then A will no longer be invoked after B, but after C.

class A(object):
def __init__(self, *args, **kwargs):
print('__init__ is called in A')
super(A, self).__init__(*args, **kwargs)

class B(object):
def __init__(self, *args, **kwargs):
print('__init__ is called in B')
super(B, self).__init__(*args, **kwargs)

class AB(B,A):
def __init__(self, *args, **kwargs):
print('__init__ is called in AB')
super(AB, self).__init__(*args, **kwargs)

class C(A):
def __init__(self, *args, **kwargs):
print('__init__ is called in C')
super(C, self).__init__(*args, **kwargs)

class D(B):
def __init__(self, *args, **kwargs):
print('__init__ is called in D')
super(D, self).__init__(*args, **kwargs)

class CD(D,C):
def __init__(self, *args, **kwargs):
print('__init__ is called in CD')
super(CD, self).__init__(*args, **kwargs)

class ABCD(CD,AB):
def __init__(self, *args, **kwargs):
print('__init__ is called in ABCD')
super(ABCD, self).__init__(*args, **kwargs)

>>> abcd = ABCD()
__init__ is called in ABCD
__init__ is called in CD
__init__ is called in D
__init__ is called in AB
__init__ is called in B
__init__ is called in C
__init__ is called in A

So I think it's a good idea to think about using delegation instead of inheritance here.

class AB(object):
def __init__(self, x, y, z=0):
self.a = A(x,y)
self.b = B(z)

So, you just create a and b instances of A and B classes inside AB object. And then may use them as you need through methods by referring to self.a and self.b.

To use or not delegation depends on your case which is not clear from your question. But it may be an option to consider.

Reason for calling super class constructor using superclass.__init()__ instead of superclass()

As I understand from the book, if Sub inherits Super then one need not call superclass' (Super's) __init__() method.

This is misleading. It's true that you aren't required to call the superclass's __init__ method—but if you don't, whatever it does in __init__ never happens. And for normal classes, all of that needs to be done. It is occasionally useful, usually when a class wasn't designed to be inherited from, like this:

class Rot13Reader:
def __init__(self, filename):
self.file = open(filename):
def close(self):
self.file.close()
def dostuff(self):
line = next(file)
return codecs.encode(line, 'rot13')

Imagine that you want all the behavior of this class, but with a string rather than a file. The only way to do that is to skip the open:

class LocalRot13Reader(Rot13Reader):
def __init__(self, s):
# don't call super().__init__, because we don't have a filename to open
# instead, set up self.file with something else
self.file = io.StringIO(s)

Here, we wanted to avoid the self.file assignment in the superclass. In your case—as with almost all classes you're ever going to write—you don't want to avoid the self.name assignment in the superclass. That's why, even though Python allows you to not call the superclass's __init__, you almost always call it.

Notice that there's nothing special about __init__ here. For example, we can override dostuff to call the base class's version and then do extra stuff:

def dostuff(self):
result = super().dostuff()
return result.upper()

… or we can override close and intentionally not call the base class:

def close(self):
# do nothing, including no super, because we borrowed our file

The only difference is that good reasons to avoid calling the base class tend to be much more common in normal methods than in __init__.


Question: Why do I need to call Super's __init__ using Super.__init__(self, name) OR super(Sub, self).__init__(name) instead of a direct call Super(name)?

Because these do very different things.

Super(name) constructs a new Super instance, calls __init__(name) on it, and returns it to you. And you then ignore that value.

In particular, Super.__init__ does get called one time either way—but the self it gets called with is that new Super instance, that you're just going to throw away, in the Super(name) case, while it's your own self in the super(Sub, self).__init__(name) case.

So, in the first case, it sets the name attribute on some other object that gets thrown away, and nobody ever sets it on your object, which is why self.name later raises an AttributeError.

It might help you understand this if you add something to both class's __init__ methods to show which instance is involved:

class Super:
def __init__(self,name):
print(f"Inside Super __init__ for {self}")
self.name=name
print("Name is:",name)

class Sub(Super):
def __init__(self,name):
print(f"Inside Sub __init__ for {self}")
# line you want to experiment with goes here.

If that last line is super().__init__(name), super(Sub, self).__init__name), or Super.__init__(self, name), you will see something like this:

Inside Sub __init__ for <__main__.Sub object at 0x10f7a9e80>
Inside Super __init__ for <__main__.Sub object at 0x10f7a9e80>

Notice that it's the same object, the Sub at address 0x10f7a9e80, in both cases.

… but if that last line is Super(name):

Inside Sub __init__ for <__main__.Sub object at 0x10f7a9ea0>
Inside Super __init__ for <__main__.Super object at 0x10f7a9ec0>

Now we have two different objects, at different addresses 0x10f7a9ea0 and 0x10f7a9ec0, and with different types.


If you're curious about what the magic all looks like under the covers, Super(name) does something like this (oversimplifying a bit and skipping over some steps1):

_newobj = Super.__new__(Super)
if isinstance(_newobj, Super):
Super.__init__(_newobj, name)

… while super(Sub, self).__init__(name) does something like this:

_basecls = magically_find_next_class_in_mro(Sub)
_basecls.__init__(self, name)

As a side note, if a book is telling you to use super(Sub, self).__init__(name) or Super.__init__(self, name), it's probably an obsolete book written for Python 2.

In Python 3, you just do this:

  • super().__init__(name): Calls the correct next superclass by method resolution order. You almost always want this.
  • super(Sub, self).__init__(name): Calls the correct next superclass—unless you make a mistake and get Sub wrong there. You only need this if you're writing dual-version code that has to run in 2.7 as well as 3.x.
  • Super.__init__(self, name): Calls Super, whether it's the correct next superclass or not. You only need this if the method resolution order is wrong and you have to work around it.2

If you want to understand more, it's all in the docs, but it can be a bit daunting:

  • __new__
  • __init__
  • super (also see Raymond Hettinger's blog post)
  • method invocation (also see the HOWTO)

The original introduction to super, __new__, and all the related features was very helpful to me in understanding all of this. I'm not sure if it'll be as helpful to someone who's not coming at this already understanding old-style Python classes, but it's pretty well written, and Guido (obviously) knows what he's talking about, so it might be worth reading.


1. The biggest cheat in this explanation is that super actually returns a proxy object that acts like _baseclass bound to self in the same way methods are bound, which can be used to bind methods, like __init__. This is useful/interesting knowledge if you know how methods work, but probably just extra confusion if you don't.

2. … or if you're working with old-style classes, which don't support super (or proper method-resolution order). This never comes up in Python 3, which doesn't have old-style classes. But, unfortunately, you will see it in lots of tkinter examples, because the best tutorial is still Effbot's, which was written for Python 2.3, when Tkinter was all old-style classes, and has never been updated.

Python super constructors -- which arguments do you put in the child method?

Just pass name and colour.

class Pig(Animal):
def __init__(self, name, colour, tailType):
super().__init__(name, colour)
self.tailType = tailType

Think of it as super() provides me the method from the parent, bound to my object. What does it take as inputs? name and colour? Let's pass it name and colour, then.

And yes, __str__ is inherited like any other method.

Chain-calling parent initialisers in python

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".

Does it matter if we call the superclass constructor in a subclass as first line?

Sure there can be differences, but if you know what you are doing it is fine to do
soever.

class MyClass:
def __init__(self):
self.a = 1

class MySubClass(MyClass):
def __init__(self):
self.a = 2
MyClass.__init__(self)

vs

class MySubClass(MyClass):
def __init__(self):
MyClass.__init__(self)
self.a = 2

Result value in self.a will be different, but if it is intended, it may be valid code.

Calling a parent class constructor from a child class in python

Python recommends using super().

Python 2:

super(Instructor, self).__init__(name, year)

Python 3:

super().__init__(name, year)

python 2.7 - how to call parent class constructor

You have 2 options

old style class, you should call the super constructor directly.

class FileUtil():
def __init__(self):
pass

class Myfile(FileUtil):
def __init__(self, extension):
FileUtil.__init__(self)

new style class, inherit from object in your base class and your current call to super will be processed correctly.

class FileUtil(object):
def __init__(self):
pass

class Myfile(FileUtil):
def __init__(self, extension):
super(Myfile, self).__init__()

Is it necessary to call super().__init__() explicitly in python?

If you override the __init__ method of the superclass, then the __init__ method of the subclass needs to explicitly call it if that is the intended behavior, yes.

Your mental model of __init__ is incorrect; it is not the constructor method, it is a hook which the constructor method calls to let you customize object initialization easily. (The actual constructor is called __new__ but you don't need to know this, and will probably never need to interact with it directly, let alone change it.)

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.



Related Topics



Leave a reply



Submit