super() Method and Function Overriding in Python
This post explains the usage of super() method in function overriding, a concept that is often useful in inheritance. It also provides an example to explain how these overridden function can be invoked.
Prerequisites: Basic idea of classes and objects and inheritance in Python.
NOTE: All programs in this post are in Python 2.7.x
What is Function Overriding?
When there is an inheritance hierarchy and a derived class defines a method with the same name as a function in its base class, there is overriding. The function defined in the derived class hides its definition in the base class. Hence in general, when a member function is called, the definition in the derived class is used. For more details, refer: method overriding in Python
Do keep in mind that, we cannot override a private method of the base class.
Why Function Overriding?
The derived classes must be able to update the functions of the base class. This is, in fact, one of the uses of inheritance. It is often used to update the features defined in the base class by simply overriding its members without disturbing the structure of the base class. This ensures that the changes being made do not reflect in other derived classes of the same base class. Other derived classes may inherit the same base class for a different purpose and might need to use the same old definition. Hence this way, changes in definition (or redefinition) are made only in the relevant derived class.
Program to Illustrate Function Overriding in Python
Consider the following program,
class A(object): def __init__(self): val = 98 def foo(self): print "Method defined in A" class B(A): def __init__(self): an_val = 75 def foo(self): print "Method defined in B" def bar(self): self.foo() obj = B() obj.foo() obj.bar()
The above program is a case of single-inheritance where B is a derived class deriving from class A.
The function foo() defined in the base class A is redefined or in other words, overridden by class B. Notice that the function header is the same for the function in both the classes. However, the only difference is the function body. There is another function bar() in the derived class, which calls the overridden function using the self keyword
Now let us take a look at the output of this program
Notice that in both the calls to the function foo(), from outside as well as from inside the class, the function defined in the derived class only gets invoked. Hence in this manner, the derived class is able to override the function definition in the base class. But what if you have to call the base class’ version of the function foo(). One obvious method is to create an object of A and call it through that. However, the next section is all about calling the base-class’ overridden function using the derived class’ object.
REMARK: Notice that the base class A is defined as a derived class of another in-built class ‘object’. This is necessary for new-style classes in python.
Invoking Overridden Functions in Python
What is super()?
The super() method can be used only for new-style classes in Python (see the remark in the previous section).
According to the Python documentation,
super(), returns a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.
In simple words, it is used to refer to its immediate super-class or parent-class. It is considered a better method to access data members and member functions of the base class, rather than using the base class name itself. super() method in function overriding is one of the most common applications of this new-class method.
Syntax: super(<own_class_name>,self).<function_to_be_called>(<args...>) #Python 2.7.x
super() Method in Function Overridng
super() method in function overriding can be used to call the overridden function in the follwing manner,
Consider the following code,
class A(object): def __init__(self): val = 98 def foo(self): print "Method defined in A" class B(A): def __init__(self): an_val = 75 def foo(self): print "Method defined in B" def bar(self): self.foo() super(B,self).foo() obj = B() obj.foo() obj.bar()
Notice the changes made to the function bar(). We have added a line that uses super() to call the function foo() defined in its base class.
The output is as follows:
Hence this way we are able to call the overridden function of the base class A.
NOTE: The derivation of class A from the built-in ‘object’ class is necessary to use super() method
The python classes which do not directly or indirectly inherit from the built-in ‘object’ class are called old-style classes. These classes cannot use some of the newer features of classes such as super(), __mro__, etc.
However, there is a method to invoke the overridden function of the base class through a derived class object, even for the old-style clases.
Consider the following code:
class A: def __init__(self): val = 98 def foo(self): print "Method defined in A" class B(A): def __init__(self): an_val = 75 def foo(self): print "Method defined in B" def bar(self): self.foo() A.foo() obj = B() obj.foo() obj.bar()
Notice that the only difference i this method, compared to the super method is that, we are explicitly mentioning the class (‘A’ in this case) whose definition for the function foo() needs to be used. Hence, we are able to directly access the overridden function of the base class. The output is exactly the same as before. This method is useful when one has no choice but to use old-style classes. More importantly, it is useful if one has to directly access the penultimate base class’ function definition in case of a multilevel inheritance. This case is dealt with in the next section.
Old-Class Method for Multilevel Inheritance
Suppose there is a class A. Another class B inherits from A and class C inherits from B. This is a typical case of multilevel inheritance. Let us say, the function foo() is defined differently (i.e overridden) in each of the three classes. Suppose we want to call class A’s copy of the function through class C’s object, the old-class method is the easiest.
class A(object): def __init__(self): val = 98 def foo(self): print "Method defined in A" class B(A): def __init__(self): an_val = 75 def foo(self): print "Method defined in B" def bar(self): self.foo() class C(B): def __init__(self): an_val = 75 def foo(self): print "Method defined in C" def bar(self): self.foo() A.foo(self) super(C,self).foo() obj = C() obj.foo() obj.bar()
Here we have directly mentioned class A while calling the foo() function and hence it will be invoked. However, in the next line, we have used super() to illustrate that this will only call the copy of foo() in its immediate super-class i.e B.
Here is the output
This is one situation where it is convenient to use the old-class style. However, the use of super() is always preferred and encouraged over the old-class style for a variety of reasons say, for instance, it has a more sensible Method Resolution Order (MRO). After all, it is an update made to the language in order to overcome the glitches caused by the old-style classes.