Why Is Super Class Constructor Always Called

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.

Is it unnecessary to put super() in constructor?

Firstly some terminology:

  • No-args constructor: a constructor with no parameters;
  • Accessible no-args constructor: a no-args constructor in the superclass visible to the subclass. That means it is either public or protected or, if both classes are in the same package, package access; and
  • Default constructor: the public no-args constructor added by the compiler when there is no explicit constructor in the class.

So all classes have at least one constructor.

Subclasses constructors may specify as the first thing they do which constructor in the superclass to invoke before executing the code in the subclass's constructor.

If the subclass constructor does not specify which superclass constructor to invoke then the compiler will automatically call the accessible no-args constructor in the superclass.

If the superclass has no no-arg constructor or it isn't accessible then not specifying the superclass constructor to be called (in the subclass constructor) is a compiler error so it must be specified.

For example:

public class Base { }
public class Derived extends Base { }

This is fine because if you add no constructor explicitly Java puts in a public default constructor for you.

public class Base { }
public class Derived extends Base { public Derived(int i) { } }

Also fine.

public class Base { public Base(String s) { } }
public class Derived extends Base { }

The above is a compilation error as Base has no default constructor.

public class Base { private Base() { } }
public class Derived extends Base { }

This is also an error because Base's no-args constructor is private.

Will the base class constructor be automatically called?

This is simply how C# is going to work. The constructors for each type in the type hierarchy will be called in the order of Most Base -> Most Derived.

So in your particular instance, it calls Person(), and then Customer() in the constructor orders. The reason why you need to sometimes use the base constructor is when the constructors below the current type need additional parameters. For example:

public class Base
{
public int SomeNumber { get; set; }

public Base(int someNumber)
{
SomeNumber = someNumber;
}
}

public class AlwaysThreeDerived : Base
{
public AlwaysThreeDerived()
: base(3)
{
}
}

In order to construct an AlwaysThreeDerived object, it has a parameterless constructor. However, the Base type does not. So in order to create a parametersless constructor, you need to provide an argument to the base constuctor, which you can do with the base implementation.

Why call super() in a constructor?

There is an implicit call to super() with no arguments for all classes that have a parent - which is every user defined class in Java - so calling it explicitly is usually not required. However, you may use the call to super() with arguments if the parent's constructor takes parameters, and you wish to specify them. Moreover, if the parent's constructor takes parameters, and it has no default parameter-less constructor, you will need to call super() with argument(s).

An example, where the explicit call to super() gives you some extra control over the title of the frame:

class MyFrame extends JFrame
{
public MyFrame() {
super("My Window Title");
...
}
}

Why is constructor of super class invoked when we declare the object of sub class?

Because it will ensure that when a constructor is invoked, it can rely on all the fields in its superclass being initialised.

see 3.4.4 in here

What are the rules for calling the base class constructor?

Base class constructors are automatically called for you if they have no argument. If you want to call a superclass constructor with an argument, you must use the subclass's constructor initialization list. Unlike Java, C++ supports multiple inheritance (for better or worse), so the base class must be referred to by name, rather than "super()".

class SuperClass
{
public:

SuperClass(int foo)
{
// do something with foo
}
};

class SubClass : public SuperClass
{
public:

SubClass(int foo, int bar)
: SuperClass(foo) // Call the superclass constructor in the subclass' initialization list.
{
// do something with bar
}
};

More info on the constructor's initialization list here and here.



Related Topics



Leave a reply



Submit