Python Inheritance

    Inheritance is one of the features of Object-Oriented Programming. According to this feature, the child class can inherit or access the public properties and methods of the parent class. In Python, we do not have access modifiers to separate public or private properties of a parent class, this means, in Python, the child class can inherit all the properties and methods of its parent class. The parent class and child class terminologies are also referred to as base class and drive class. This Python tutorial will teach Python inheritance , method overloading, method overriding, inheritance types, and Method Resolution order.

    What is Inheritance in Python?

    As we know that inheritance is one of the features of OOP and it is used too often. According to the inheritance concept we can use the properties of one class in another class, and these properties could be its class methods and attributes. In Inheritance, we require at least two classes base and derived . The base class will be a normal class but the derived class will have some modification in its syntax. We could also consider base class as a parent class and derived class as a child class.

    Python Inheritance Syntax

    class base:
        # base class block
    
    class derived(base):
        # derived class block

    To inherit the base class we pass the base class name as an argument to the derived class.

    Inheritance example

    #base or parent class
    class Student:          
        def get(self):
            self.roll = int(input("Please enter the roll no.: "))
            self.m1 = int(input("Please enter the M1 marks: "))
            self.m2 = int(input("Please enter the M2 marks: "))
            self.ea = int(input("Please enter the extra activity marks: "))
    
    # Derived or child class
    class Output(Student):                               
        def display(self):
            #accessing parent class attributes or variables
            self.total = self.m1 + self.m2 + self.ea
            print("Roll No.",self.roll, "total marks are: ", self.total )
    
    student =  Output()
    student.get()
    student.display()

    #Output

    Please enter the roll no.: 10
    Please enter the M1 marks: 95
    Please enter the M2 marks: 94
    Please enter the extra activity marks: 98
    Roll No. 10 total marks are:  28

    Behind the code

    In the above example, we have defined 2 classes Student and Output , here Student is the base class whereas Output is the derived class which means the Output class can inherit the properties of the Student class. In Student class we have defined all the attributes ( roll , m1 , m2 , and ea ) inside the get () method and access those attributes in the Output class display() method where we make the total . In the above program we did not create the object of Student class only with the help of Output object student we accessed the Student get() method to take input from the user and use those inputs in display() method. This inherits property used by Output class in which it accessed the Student class attributes and get () method prove that in python class can follow the inheritance property. The main objective of inheritance in Python or any other programming language is the reusability of code, by inheriting the parent class we do not need to write the same logic for the child class. This type of technique comes very handy in big projects where many sub-sections require the same properties as the main section.

    Types of Inheritance

    The type of inheritance in Python depends upon the relationship between the chile and parent classes. There are five types of inheritance in Python.

    1. Single Inheritance
    2. Multiple Inheritance
    3. Multilevel Inheritance
    4. Hierarchical Inheritance
    5. Hybrid Inheritance

    Now let’s discuss all these inheritance types one by one with examples

    Single Inheritance In Python

    In Single Inheritance, there is one parent class and one child class, and the child class directly inherits from the single parent class.

    Single Inheritance Syntax

    class parent():
        #parent class body
    
    class child(parent):
        #child class body

    Example

    Let’s create two classes Car and BMW, and perform single inheritance where the BMW class inherit the Car class.

    #parent class
    class Car():
        def car_wheel(self):
            print("This car has 4 wheelss")
    
    #child class inheriting Car Class
    class BMW(Car):
        def model_info(self):
            print("This is a BMW M1")
    
    #create an object of child class BMW
    bmw = BMW()
    
    #access the Car method using bmw object
    bmw.car_wheel()
    
    #access the BMW method using bmw object
    bmw.model_info()

    Output

    This car has 4 wheelss
    This is a BMW M1

    Behind the code

    In the above example, we perform the single inheritance. The Car was the base class that was inherited by the single drive class BMW.

    Multiple Inheritance In Python

    In multiple inheritance, a single child class can inherit from two or more than two parents classes.

    Multiple Inheritance Syntax

    class parent1():
        #parent1 class body
    
    class parent2():
        #parent2 class body
    
    class child(parent1, parent2):
        #child class body

    Example

    Let’s write a script in Python that demonstrates Python Multiple Inheritance, for multiple inheritance, there must be at least two parent classes.

    class RTO():
        def car_current_owner(self, name):
            print("This car owner is :", name)
    
    #parent class
    class Car():
        def car_wheel(self):
            print("This car has 4 wheels")
    
    #child class inheriting Car and RTO classes
    class BMW(Car, RTO):
        def model_info(self):
            print("This is a BMW M1")
    
    #create an object of child class BMW
    bmw = BMW()
    
    #access the Car method using bmw object
    bmw.car_wheel()
    
    #access the RTO car_registary() method with bmw object
    bmw.car_current_owner('Rohan')
    
    #access the BMW method using bmw object
    bmw.model_info()

    Output

    This car has 4 wheels
    This car owner is: Rohan
    This is a BMW M1

    Behind the code

    In this multiple inheritance example, the child class BMW inherited properties from two parent classes RTO and Car. Because of multiple inheritances, bmw object is able to access the car_wheel() and car_current_owner() methods which belong to the parent class.

    Multilevel Inheritance In Python

    In Multilevel Inheritance, the child class can be the parent class of another class. Let’s say we have three classes, A, B, and C. Where A is the parent class of B and B is the parent class of C. Making B the child and C the grandchild of class A. Here A does not have any parent class which makes a SuperClass.

    Multilevel Inheritance Syntax

    class parent():
        #superclass body
    
    class child1(parent):
        #child  1 body
    
    class child2(child1):
        #child 2 body

    In multilevel inheritance, the last child ultimately inherits the properties of all the above parents.

    Multilevel Inheritance Example

    #parent class
    class Car():
        def car_wheel(self):
            print("This car has 4 wheels")
    
    #child class inheriting Car class
    class BMW(Car):
        def model_info(self):
            print("This is a BMW M1")
    
    #child class inheriting BMW classes
    class Showroom(BMW):
        def bmw_car(self):
            print("Only One BMW car is available")
    
    #create an object of child class Showroom
    showroom = Showroom()
    
    #access Showroom bmw_car() method
    showroom.bmw_car()
    
    #access the BMW method using showroom object
    showroom.model_info()
    
    #access the Car method using showroom object
    showroom.car_wheel()

    Output

    Only One BMW car is available
    This is a BMW M1
    This car has 4 wheels

    Behind the code

    In this example, we perform multilevel inheritance using Car , BMW , and Showroom classes. The attribute access in Multilevel inheritance flow from down to up, or from child to parent. Similarly in the above example, the Showroom class is able to inherit BMW , as well as Car properties.

    Hierarchical Inheritance In Python

    In Hierarchical inheritance, we have one parent class that is inherited by two or more two-child classes.

    Hierarchical Inheritance Syntax

    class parent():
        #parent class body
    
    class child1(parent):
        #child1 class body
    
    class child2(parent):
        #child2 class body
    
    class child3(parent):
        #child3 class body

    Hierarchical Inheritance Example

    #parent class
    class Car():
        def car_wheel(self):
            print("This car has 4 wheels")
    
    #child class inheriting Car class
    class BMW(Car):
        def model_info(self):
            print("This is a BMW M1")
    
    #child class inheriting Car class
    class Audi(Car):
        def model_info(self):
            print("This is an Audi A6 Sedan")
    
    #child class inheriting Car class
    class Mercedes(Car):
        def model_info(self):
            print("This is a Mercedes Benz E Class")
    
    #create an object of child class BMW
    bmw = BMW()
    bmw.model_info()
    bmw.car_wheel()
    
    #create an object of child class Audi
    audi = Audi()
    audi.model_info()
    audi.car_wheel()

    Output

    This is a BMW M1
    This car has 4 wheels
    This is an Audi A6 Sedan
    This car has 4 wheels

    Hybrid Inheritance In Python

    In Hybrid inheritance, the relationship between different classes is a combination of all the inheritance we have discussed in the above examples.

    Syntax

    class parent():
        #parent class body
    
    class child1(parent):
        #child1 class body
    
    class child2(parent):
        #child2 class body
    
    class child3(child1, child2):
        #child 3 class body

    Hybrid Inheritance Example

    #parent class
    class Car():
        def car_wheel(self):
            print("This car has 4 wheels")
    
    #child class inheriting Car class
    class BMW(Car):
        def model_info(self):
            print("This is a BMW M1")
    
    #child class inheriting Car class
    class Service(Car):
        def service_info(self):
            print("Car Service is also available")
    
    #child class inheriting BMW and Service classes
    class Showroom(BMW, Service):
        def availability(self):
            print("Only one BMW car is available right now")
    
    #creating the object of Showroom
    showroom = Showroom()
    
    #access Showroom's method with showroom object
    showroom.availability()
    
    #access BMW's method with showroom object
    showroom.model_info()
    
    #access Service's method with showroom object
    showroom.service_info()
    
    #access Car's method with showroom object
    showroom.car_wheel()

    Output

    Only one BMW car is available right now
    This is a BMW M1
    Car Service is also available
    This car has 4 wheels

    Python Super function

    When a child class inherits a parent class, the child class becomes the subclass of the parent class, where the parent class is known as a superclass. When we want to call or access a parent class method inside a child class, we can either use the object or the name of the parent class. But Python provides a super() function, which creates a proxy or temporary object of the parent class so we can access the parent class method inside the child class.

    Benefits of using the super() function

    1. With the super function, we do not need to use the parent class name while accessing the parent methods.
    2. It can work with both single and multiple inheritance.
    3. It comes very handy during function or method overloading.

    Super Function with Single inheritance

    In single inheritance, the super() function directly refers to the superclass of the child method that is the parent class.

    Example

    #parent class
    class Car():
        def __init__(self):
            print("This vehicle is a car ")
    
    #child class
    class BMW(Car):
        def __init__(self):
            print("This is a BMW car")
    
            #call the super class(Car) init method 
            super().__init__()
    
    #create the object of BMW class
    bmw = BMW()

    Output

    This is a BMW car
    This vehicle is a car

    Break the code

    In the above example when we created the object of BMW() class bmw, it automatically call its __init__() function. And inside its __init__() function we have called its superclass’s ( Car ) __init__() function using super().__init__() statement.

    Super Function with Multiple inheritances

    In multiple inheritances, the super function refers to the superclass according to the Method Resolution Order.

    Example

    #parent class
    class RTO():
        def __init__(self):
            print("This Car comes under RTO")
    
    #child class
    class Car():
        def __init__(self):
            print("This vehicle is a car ")
    
    
            #here super is the object of BMW
            #when it is called by BMW object
            #else it is the object of Car itself
            super().__init__()
    
    #child class
    class BMW(Car, RTO):
        def __init__(self):
            print("This is a BMW car")
    
            #call the super classes Car
            super().__init__()
    
    #create the object of BMW class
    bmw = BMW()

    Output

    This is a BMW car
    This vehicle is a car 
    This Car comes under RTO

    Break the code

    In the above example the super().__init__() statement of the BMW class call the Car’s class, and the super().__init__() statement inside the Car’s class call the RTO’s class __init__() method.

    Method Overriding

    When a child class inherits a parent class, it inherits the parent’s all properties such as methods and attributes. But if any method of the parent class does not satisfy the child class, the child class can have a method of itself with the same method name as the parent class. This redefining of the same method in the child class is known as method overriding .

    Let’s understand it with an example:

    class Animal:
        def __init__(self,name):
            self.name= name
            print(self.name,"Animal")
    
        def speak(self):
            print(" Voice ")
    
    class Cat(Animal):
        def speak(self):
            print(self.name,"speak meow")
    
    tom = Cat("tom")           #this will invoke the animal __init__ method because cat does not have __init__
    tom.speak()                #this will invoke cat speak method

    #Output

    tom Animal
    tom speak meow

    Behind the Code

    In the above code when we create the Cat classe’s object tom and we pass a name “tom” along with the class cat(“tom”) this will invoke the Animal class constructor that is __init__() because the cat class does not have any __init__ method. But when we use the tom object and call the speak method then the Cat class’s speak() method gets invoked not the Animal class speak() method why so? because the interpreter first looks at all the methods of Cat class if the Cat class did not have the speak method then it would call the Animal speak method. The calling of methods by the objects depends upon the Method Resolution Order now let’s discuss what it is?

    What is Method Resolution Order in Python?

    The Method Resolution Order is an order followed by Python to look up for the methods or attributes of a class object. The order generally goes from down to up, which means from child to parent. The MRO plays a very crucial role in multiple inheritance and method overriding. The Method Resolution Order of a Class can be seen using the mro() method.

    Example

    Let’s see an example of multiple inheritance and how the order of method accessing followed by it.

    class A():
        def method(self):
            print("This method is inside class A")
    
    class B():
        def method(self):
            print("This method is inside class B")
    
    class C(B, A):
        def method(self):
            print("This method is inside class C")
    
    #create the object of class C
    c = C()
    
    #call the method() on object c
    #it will call the method defined inside the C class
    c.method()
    
    #print the Method Resolution Order
    print(C.mro())

    Output

    This method is inside class C
    [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

    Break the code

    In the above example, the C inherits from B and A , that’s why when we call the method() method on class object c it calls the method() inside the Class C because it has high priority. The output of C.mro() list shows the order in which the c object should look for the methods. While initializing the class C, we set inheritance as ( B, A) , and because Python read from left to right B has the higher priority than A .