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.
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 themake_sound()
method, the implementation in theAnimal
class is executed. - When we create an instance of the
Dog
class and call themake_sound()
method, the overridden implementation in theDog
class is executed instead.
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
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.
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.
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
andSuperClass2
define a methodinfo()
. - 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
.