Skip to main content

Python Inheritance and Multiple Inheritance

In Python, inheritance allows a class to inherit attributes and methods from another class. It is a fundamental concept in object-oriented programming that allows code reuse and modularity.

Python Inheritance

When a class inherits from another class, it is called the child class or subclass or derived class, and the class it inherits from is called the parent class or superclass or parent.

The child class inherits all the attributes and methods of the parent class, and it can also add its own attributes and methods or override the ones inherited from the parent class.

Syntax

# define a superclass
class super_class:
# attributes and method definition

# inheritance
class sub_class(super_class):
# attributes and method of super_class
# attributes and method of sub_class

Example

A more complex example with Animal, Dog, Cat classes:

class Animal:
def __init__(self, name):
self.name = name

def speak(self):
print("The animal makes a sound.")

class Dog(Animal):
def speak(self):
print("The dog barks.")

class Cat(Animal):
def speak(self):
print("The cat meows.")

In this example, the parent class is Animal while Dog and Cat classes are subclasses of Animal.

Note that in this example each subclass overrides the speak() method that it inherited from the parent class.

To create objects of the child classes and invoke their methods, you can do as follows:

dog = Dog("Buddy")
cat = Cat("Whiskers")

dog.speak() # Output: The dog barks.
cat.speak() # Output: The cat meows.

IS-A relationship

In Python, inheritance represents an "is-a" relationship between classes. This means that a subclass is considered to be a specialized version of its superclass.

The "is-a" relationship can be understood as a hierarchical structure, where the subclass inherits the properties and behaviors of the superclass, allowing for code reuse and modular design.

Some examples of "is-a" relationships are:

  • Dog is an Animal
  • Apple is a Fruit
  • Car is a Vehicle

Consider the first one: "Dog is an Animal". We can code it is as follows:

class Animal:
def eat(self):
print("The animal is eating.")

class Dog(Animal):
def bark(self):
print("The dog is barking.")

We have that the Dog class inherits from the Animal class, and so we can say that a dog "is-a" type of animal. Moreover, Dog class inherits the eat() method from the Animal class, as well as any other attributes or methods defined in the Animal class.

note

By using inheritance, we can create specialized classes that inherit the common behavior from a more general class. This allows us to write code that is more concise, maintainable, and follows the principle of code reuse.

Method Overriding in Python Inheritance

Method overriding in Python inheritance allows a subclass to provide a different implementation of a method that is already defined in its superclass.

When a method is overridden, the version of the method in the subclass is used instead of the one in the superclass when the method is called on an instance of the subclass.

For example

class Animal:
def make_sound(self):
print("The animal makes a sound.")

class Dog(Animal):
def make_sound(self):
print("The dog barks.")

animal = Animal()
animal.make_sound() # Output: The animal makes a sound.

dog = Dog()
dog.make_sound() # Output: The dog barks.

In this example, we have a parent class called Animal with a method make_sound(). The Dog class is a subclass of Animal and it overrides the make_sound() method with its own implementation.

Note that:

  • When we create an instance of the Animal class and call the make_sound() method, the implementation in the Animal class is executed.
  • When we create an instance of the Dog class and call the make_sound() method, the overridden implementation in the Dog class is executed instead.
warning

Method overriding requires the method name and parameters to be the same in both the superclass and the subclass!

The overriding method in the subclass can have a different implementation or behavior, but the method signature must match.

super() method in Python Inheritance

The super() method is used to access and invoke the superclass method from the subclass.

This is useful when you want to extend or modify the behavior of the superclass's method without completely overriding it.

For example:

class Animal:
def make_sound(self):
print("The animal makes a sound.")

class Dog(Animal):
def make_sound(self):
super().make_sound() # Calling the superclass's method
print("The dog barks.")

dog = Dog()
dog.make_sound()

Output:

The animal makes a sound.
The dog barks.

In this example, the Dog class is a subclass of the Animal class and Dog class overrides the make_sound() method, but it also calls the superclass's make_sound() method using super().make_sound().

Python Multiple Inheritance

Multiple inheritance in Python allows a class to inherit attributes and methods from multiple parent classes.

So, a subclass can have multiple superclasses, and it can inherit and utilize the properties and behaviors from all of them.

Syntax of Multiple Inheritance

To define a class with multiple inheritance, you list the parent classes in the class definition, separated by commas.

class ParentClass1:
def method1(self):
print("Method 1 from ParentClass1")

class ParentClass2:
def method2(self):
print("Method 2 from ParentClass2")

class ChildClass(ParentClass1, ParentClass2):
def method3(self):
print("Method 3 from ChildClass")

In this example, we have two parent classes, ParentClass1 and ParentClass2, each with their own methods. The ChildClass inherits from both parent classes using multiple inheritance.

You can access methods from both parent classes in this way:

child = ChildClass()
child.method1() # Output: Method 1 from ParentClass1
child.method2() # Output: Method 2 from ParentClass2
child.method3() # Output: Method 3 from ChildClass
warning

Multiple inheritance is powerful because it allows a class to inherit and combine the attributes and behaviors from multiple parent classes.

However, it also introduces some complexities, such as potential conflicts if multiple parent classes have methods with the same name.

note

In cases where multiple parent classes have methods with the same name, the Method Resolution Order (MRO) determines which method is called.

Python uses the C3 linearization algorithm to calculate the MRO and resolve any conflicts.

Python Multilevel Inheritance

Multilevel inheritance in Python is the process where a class is derived from another class, which itself is derived from another class: this creates a chain of inheritance.

Each derived class inherits attributes and methods from its immediate parent class and all the ancestor classes up the hierarchy.

For example:

class Grandparent:
def grandparent_method(self):
print("Called from Grandparent")

class Parent(Grandparent):
def parent_method(self):
print("Called from Parent")

class Child(Parent):
def child_method(self):
print("Called from Child")

# Create an instance of Child
c = Child()

# Call methods from each class
c.grandparent_method() # Output: Called from Grandparent
c.parent_method() # Output: Called from Parent
c.child_method() # Output: Called from Child

In this example, Child class is derived from Parent class, which is derived from Grandparent class. Notice that an instance of Child can access methods defined in all three classes.

Method Resolution Order (MRO) in Python

Method Resolution Order (MRO) in Python determines the order in which the base classes are searched when looking for a method to execute. This

It is fundamental when dealing with multiple inheritance because it specifies the sequence in which the parent classes are searched for a method. If a method is not found in the current class, Python will follow the MRO to check the parent classes. Also, it is useful when a derived class inherits from multiple superclasses and they have methods with the same name.

note

The MRO follows certain rules:

  • Children of a class come before their parents.
  • Left parents come before right parents.
  • A class only appears once in the MRO.

If these rules cannot be satisfied, Python raises an error. The actual MRO is computed using the C3 Linearization algorithm

For example:

class SuperClass1:
def info(self):
print("Super Class 1 method called")

class SuperClass2:
def info(self):
print("Super Class 2 method called")

class Derived(SuperClass1, SuperClass2):
pass

d1 = Derived()
d1.info()

# Output: "Super Class 1 method called"

In this example:

  • Both classes SuperClass1 and SuperClass2 define a method info().
  • Then, when info() is called using the d1 object of the Derived class, Python uses the MRO to determine which method to call.

Since the MRO specifies that methods should be inherited from the leftmost superclass first, in this example info() of SuperClass1 is called rather than that of SuperClass2.