Python Inheritance Complete Tutorial

  • Post author:
  • Post category:Python

In this section, we will learn what the Inheritance is and how it works in Python.

Python Class Inheritance

Class inheritance is the idea of using the members (attribute and methods) of one class as the members of another class!

Basically, using inheritance, we can share the members of classes between each other.

This is especially helpful when we want to create a class with a set of attributes and methods that are already existing in other classes! So instead of inventing the wheel and designing a class from scratch, we can make the new class to inherit from the old classes (those that have the needed methods and attributes) and use those members as a result of this inheritance.

Another reason for using inheritance is that we will not copy the same method in multiple different places! So in the future, if there was a need to modify a method, that will be done only in one place instead of multiple places.

Related terminology to inheritance in Python

There are a couple of terminologies that we need to be aware of when it comes to inheritance.

– Python Super class (Base class| Parent class):

This is a class with members that other classes want to use is. Or basically, this is the class that other classes inherit from.

– Python Subclass (Child class):

This is the class that inherit from other classes.

Python Inheritance: Syntax and Declaration

Alright, in order to create an inheritance, we need at least two classes. Let’s say the first class is called `Parent` and the other class is called `Child`.

So if we want the `Child` class to inherit from the `Parent` class, then the syntax would be:

class Parent: 
    #body... 
    pass

class Child (Parent):
    #body... 
    pass

Note the use of a pair of parentheses after the name of the `Child` class. In order to make a class to inherit from another class, we first put a pair of parentheses after the name of that class and then inside this class we put the class/ classes that we want the child class to inherit from.

If there are multiple classes, we want a child class to inherit from, then we should put them all in the parentheses and use a comma to separate them.

Inheritance Example in Python:

class GrandParent:
    def grandParentMessage(self):
        print("This message is coming from the grandparent class")

class Parent: 
    def parentMessage(self):
        print("This message is coming from the parent class")

class Child (GrandParent,Parent):
    pass


child = Child()

child.parentMessage()
child.grandParentMessage()

Output:

This message is coming from the parent class

This message is coming from the grandparent class

As mentioned before, if a child inherits from another class, it is able to access the members of that parent class. So in this example, the Child class is inheriting from the `Parent` and `GrandParent` classes. Within the body of these two classes, there are two methods ` parentMessage` and `grandParentMessage`. Now, because of inheritance, we could create an object from the `Child` class and call for the two mentioned methods.

Let’s see what exactly happens when we call a method that exists in a parent class:

Take a look at this statement:

child.parentMessage()

The execution engine always starts from the base class or the class where the instance object was created from. For example, here the object is created from the `Child` class. So first the engine starts by checking the members of this class to see if there’s any method named `parentMessage()`. If there’s one, it will run that method even if there’s the same method in the parent class or grandparent etc. Basically, the priority of the Python engine is to find the target method from the closest class and if not found then move one class at time upward until it is found or an error returns if there wasn’t any such method.

So here the `Child` class did not have the mentioned method. So next the engine will check the `GrandParent` class for the mentioned method! But this one does not have it as well! Next is the `Parent` class. Here we have the method! So the engine will call that method and run it.

Next, we have this statement:

child.grandParentMessage()

Here again, the execution engine (interpreter) checks the Child class to see if there’s a method with this name in the Child class or not. Here because there’s none, then the GrandParent class will be checked next! In the GrandParent class there’s the `grandParentMessage()` method and so Python will run this method and won’t go to the Parent class to check the body of that method as well! (Basically, the operation is done and so there’s no need to search for more methods with the same name).

Note: in the example above, the Child class did not have any member, but that’s not a rule! You are free to have your own methods in a child (derived) class as well.

Python Types of inheritance:

Inheritance has different forms and so in this section, we will explain these forms and shapes.

1- Single inheritance in Python

Single inheritance is when we have one class that is inheriting only from another class. Also, the base or the parent class does not inherit from another class in this model!

This is known as a single inheritance! Basically, there are only two classes are involved and one is inheriting from the other.

Example: single inheritance

class Parent: 
    def parentMessage(self):
        print("This message is coming from the parent class")

class Child (Parent):
    
    def childMessage(self):
        print("This message is coming from the child class")


child = Child()

child.childMessage()
child.parentMessage()

Output:

This message is coming from the child class

This message is coming from the parent class

As you can see, there are only two classes involved in the inheritance and the Child class is inheriting from the Parent. This is an example of a single inheritance.

2- Multilevel inheritance in Python

Multilevel inheritance is when we have multiple classes inheriting from each other.

For example, let’s say we have 3 classes A, B, C. Here The class C is the child class of the class B and the class B itself is the child of the class A.

In this case, the class C has the access to the members of the class B and A (the two classes in the chain of inheritance). And the class B has the access to the members of the class A.

This is called multilevel inheritance.

Example: multilevel inheritance

class GrandParent:
    def grandParentMessage(self):
        print("This message is coming from the grandparent class")

class Parent(GrandParent): 
    def parentMessage(self):
        print("This message is coming from the parent class")

class Child (Parent):
    
    def childMessage(self):
        print("This message is coming from the child class")


child = Child()

child.childMessage()
child.parentMessage()
child.grandParentMessage()

Output:

This message is coming from the child class

This message is coming from the parent class

This message is coming from the grandparent class

As you can see, the very child class in the chain has the access to the entire members of the ancestor classes.

3- Multiple Inheritance in Python

A multiple inheritance is when one class is inheriting from multiple classes at the same time.

In Multiple Inheritance, we put as many classes as the target class needs to inherit from into the pair of parentheses that come after the name of the class.

The first example at the beginning of this section is an example of multiple inheritance.

Example: multiple inheritance in Python

class GrandParent:
    def grandParentMessage(self):
        print("This message is coming from the grand parent class")

class Parent(GrandParent): 
    def parentMessage(self):
        print("This message is coming from the parent class")

class Child (GrandParent, Parent):

    def childMessage(self):
        print("This message is coming from the child class")

Note that the `Child` class is using multiple inheritance by inheriting from GrandParent and the Parent class at the same time.

Python inheritance and method overriding

Method overriding is explained in later section, but in short, it is the process of creating the same method with the same signature (name and the number of parameters), in a child class while its parent has the same method.

In a situation like this, if we create an object from the Child class and call that overridden method, Python will pick the method from the child class instead of calling the same method of the parent class!

This is because the search for a method starts from down of the chain of inheritance or simply from the class that the object is created from.

Then, if the very child class didn’t have the invoked method, in that case only, the search begins from the immediate ancestor (parent class) and moves on until the closest ancestor had the method.

So by overriding a method, we’re basically shadowing the same method in the parent class!

Example: implementing method overriding in Python

class Parent: 
    def printMessage(self):
        print("This message is coming from the parent class")

class Child (Parent):

    def printMessage(self):
        print("This message is coming from the child class")


child = Child()

child.printMessage()

Output:

This message is coming from the child class

In this example, the method `printMessage()` is used in both `Child` and `Parent` classes. Now, because the method has the same name and the same amount of parameters (only one) in both classes, then this is a method overriding! This means if we create an object from the `Child` class and call the `printMessage()` method, then only the method of the `Child` class will be invoked and the Python engine won’t run the same method in the parent class.

Note: when overriding a method, it is important that the methods have the same name and the same amount of parameters. Otherwise, if we only use the same name but with different number of parameters, it won’t be a method overriding anymore.

Python super() Function

When working with inheritance, sometimes we need to explicitly call a method of the parent class within the body of a child’s method!

If we call such method directly within the body of a child method, we will get an error (if the same method is not in the child class as well).

Now, in order to access a method of the parent class, we can use the `super()` function.

Calling this function within the body of a child method is a way of getting the access to the methods of the parent class.

So after calling this function, put a dot operator after that and then call the method you want to access.

super().functionName()

Example: using super function in Python

class Parent: 

    def printMessage(self):
        print("This message is coming from the parent class")

    def sayHi(self, fullName):
        print("Hello from the Parent method")


class Child (Parent):

    def printMessage(self):
        super().sayHi("John Doe")
        super().printMessage()
        print("This message is coming from the child class")


child = Child()

child.printMessage()

Output:

Hello from the Parent method

This message is coming from the parent class

This message is coming from the child class

Let’s take a look at the `printMessage()` method of the Child class:

def printMessage(self):
        super().sayHi("John Doe")
        super().printMessage()
        print("This message is coming from the child class")

print("This message is coming from the child class")

As you can see, here we’ve called the `sayHi()` and the `printMessage()` methods of the parent class using the `super()` function. Note that if we don’t use the super() function, we would get an error.

Python Inheritance and __init__

When working with inheritance, be aware that if the parent classes of a child class have constructor methods (The __init__ method) with parameters, you should somehow call those methods and initialize them as well!

Consider the example below:

class Parent: 

    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName

    def printFullName(self):
        print(f"{self.firstName} {self.lastName}")


class Child (Parent):

    def __init__(self, firstName, lastName):
        pass


child = Child("John","Doe")

child.printFullName()

Output:

AttributeError: 'Child' object has no attribute 'firstName'

In this example, the `Child` class is inheriting from the `Parent` class. Here we’ve created an object of type `Child` and called the `printFullName()` method.

But look that we’ve got an error!

Here’s the problem: when an object of a child class is created, the entire members of the parent classes will be initialized as well! This means if we create an object of type Child in this example, then behind the scene a call to the `Parent` class will be made and Python tries to initialize the members of the parent classes as well.

But in this example, the problem is that the `init` method of the parent class needs two arguments for the `firstName` and `lastName` parameters! So if we don’t pass these values explicitly, then there’s no way for python to figure out what to pass for these parameters.

Now, in order to solve this problem, we need to use the `super()` function as the first statement in the body of the Child `__init__` method. Basically, using the `super()` function we call the Parent’s `__init__` method and pass the required arguments as the example below shows now:

Example: Python using __init__ in inheritance

class Parent: 

    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName

    def printFullName(self):
        print(f"{self.firstName} {self.lastName}")


class Child (Parent):

    def __init__(self, firstName, lastName):
        super().__init__(firstName, lastName)


child = Child("John","Doe")

child.printFullName()

Output:

John Doe

As you can see, now the firstName and lastName of the parent class are initialized as well.

Note: just remember that if a Parent class has explicitly declared the `__init__` method, it is your responsibility to call that `init` method within the body of the `init` method of the child class and pass its required arguments properly.

Leave a Reply