Object Oriented Programming - Inheritance, Overloading and Overidding

Constructor Overloading


In [ ]:
class Student():
    def __init__(self, name, id_no=None):
        self.name = name
        self.id_no = id_no if id_no is not None else "Not Allocated"
    
    def __str__(self):
        s = self.name
        return s + "\n" + "Name : " + self.name + " , ID : " + self.id_no

    def __add__(self, a):
        return self.name + a.name
    
    def __eq__(self, a):
        return self.id_no == a.id_no
    
A = Student("Sebastin", "2015B4A70370G")
B = Student("Mayank",  "2015B4A70370G")
print(A)
print(B)
print(A + B)
print(A.__add__(B))
print(A == B)

Inheritance

Inheritance is an OOP practice where a certain class(called subclass/child class) inherits the properties namely data and behaviour of another class(called superclass/parent class). Let us see through an example.


In [ ]:
# BITSian class
class BITSian():
    def __init__(self, name, id_no, hostel):
        self.name = name
        self.id_no = id_no
        self.hostel = hostel
    
    def get_name(self):
        return self.name
    
    def get_id(self):
        return self.id_no
    
    def get_hostel(self):
        return self.hostel


# IITian class
class IITian():
    def __init__(self, name, id_no, hall):
        self.name = name
        self.id_no = id_no
        self.hall = hall
    
    def get_name(self):
        return self.name
    
    def get_id(self):
        return self.id_no
    
    def get_hall(self):
        return self.hall

While writing code you must always make sure that you keep it as concise as possible and avoid any sort of repitition. Now, we can clearly see the commonalitites between BITSian and IITian classes.

It would be natural to assume that every college student whether from BITS or IIT or pretty much any other institution in the world will have a name and a unique ID number.

Such a degree of commonality means that there could be a higher level of abstraction to describe both BITSian and IITian to a decent extent.


In [ ]:
class CollegeStudent():
    def __init__(self, name, id_no):
        self.name = name
        self.id_no = id_no
    
    def get_name(self):
        return self.name
    
    def get_id(self):
        return self.id_no

# BITSian class
class BITSian(CollegeStudent):
    def __init__(self, name, id_no, hostel):
        self.name = name
        self.id_no = id_no
        self.hostel = hostel
    
    def get_hostel(self):
        return self.hostel


# IITian class
class IITian(CollegeStudent):
    def __init__(self, name, id_no, hall):
        self.name = name
        self.id_no = id_no
        self.hall = hall
    
    def get_hall(self):
        return self.hall

In [ ]:
a = BITSian("Arif", "2015B4A70370G", "AH-5")
b = IITian("Abhishek", "2213civil32K", "Hall-10")
print(a.get_name())
print(b.get_name())
print(a.get_hostel())
print(b.get_hall())

So, the class definition is as such : class SubClassName(SuperClassName):

Using super()

The main usage of super() in Python is to refer to parent classes without naming them expicitly. This becomes really useful in multiple inheritance where you won't have to worry about parent class name.


In [ ]:
class Student():
    def __init__(self, name):
        self.name = name
        
    def get_name(self):
        return self.name

class CollegeStudent(Student):
    def __init__(self, name, id_no):
        super().__init__(name)
        self.id_no = id_no
    
    def get_id(self):
        return self.id_no
    
# BITSian class
class BITSian(CollegeStudent):
    def __init__(self, name, id_no, hostel):
        super().__init__(name, id_no)
        self.hostel = hostel
    
    def get_hostel(self):
        return self.hostel


# IITian class
class IITian(CollegeStudent):
    def __init__(self, name, id_no, hall):
        super().__init__(name, id_no)
        self.hall = hall
    
    def get_hall(self):
        return self.hall
    
a = BITSian("Arif", "2015B4A70370G", "AH-5")
b = IITian("Abhishek", "2213civil32K", "Hall-10")
print(a.get_name())
print(b.get_name())
print(a.get_hostel())
print(b.get_hall())

You may come across the following constructor call for a superclass on the net : super(self.__class__, self).__init__(). Please do not do this. It can lead to infinite recursion.

Go through this link for more clarification : Understanding Python Super with init methods

Method Overidding

This is a phenomenon where a subclass method with the same name is executed in preference to it's superclass method with a similar name.


In [ ]:
class Student():
    def __init__(self, name):
        self.name = name
        
    def get_name(self):
        return "Student : " + self.name

class CollegeStudent(Student):
    def __init__(self, name, id_no):
        super().__init__(name)
        self.id_no = id_no
    
    def get_id(self):
        return self.id_no
    
    def get_name(self):
        return "College Student : " + self.name

class BITSian(CollegeStudent):
    def __init__(self, name, id_no, hostel):
        super().__init__(name, id_no)
        self.hostel = hostel
    
    def get_hostel(self):
        return self.hostel
    
    def get_name(self):
        return "Gen BITSian --> " + self.name

class IITian(CollegeStudent):
    def __init__(self, name, id_no, hall):
        super().__init__(name, id_no)
        self.hall = hall
    
    def get_hall(self):
        return self.hall
    
    def get_name(self):
        return "IITian --> " + self.name

a = BITSian("Arif", "2015B4A70370G", "AH-5")
b = IITian("Abhishek", "2213civil32K", "Hall-10")

print(a.get_name())
print(b.get_name())
print()
print(super(BITSian, a).get_name())
print(super(IITian, b).get_name())
print(super(CollegeStudent, a).get_name())