By using inheritance, we may create classes that inherit all the features of their parent classes and allow us to add new ones.
Inheritance in Python
Inheritance in python refers to defining a new class with little or no modification to an existing class. The new class is called derived (or child) class
and the one from which it inherits is called the base(or parent) class.
Syntax: ```python class BaseClass: Body of base class
1
2
class DerivedClass(BaseClass):
Body of derived class ``` The derived class inherits the attributes and methods from the base class where new features can be added to it. This results in re-usability of code.
Example of Inheritance in Python
Let us define a polygon, a closed figure with 3 or more sides.
1
2
3
4
5
6
7
8
9
10
11
class Polygon:
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]
def inputSides(self):
self.sides = [float(input(f'Enter side {i+1}: ')) for i in range(self.n)]
def dispSides(self):
for i in range(self.n):
print(f'Side {i+1} is {self.sides[i]}')
This class provides data attributes for storing sides, a list of the n sides and the magnitude of each side. The inputSides()
method takes in the magnitude of each side and dispSides()
displays these side lengths.
A triangle is a 3-sided polygon. Consequently, we can develop a class called Triangle that descended from Polygon
. The Triangle
class now has access to all of the Polygon class’s characteristics.
We don’t have to define them once more (code reusability). Following is a definition of a triangle.
1
2
3
4
5
6
7
8
9
10
11
class Triangle(Polygon):
def __init__(self):
super().__init__(self,3)
def findArea(self):
a,b,c = self.sides
#calculate the semi-perimiter
s = (a+b+c)/2
area = (s*(s-a)*(s-b)*(s-c))**0.5
print(f'Area of the triangle: {area}')
However, class Triangle
has a new method findArea()
to find and print the area of the triangle. Here is a sample run.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
t = Triangle()
t.inputSides()
Enter side 1: 3
Enter side 2: 5
Enter side 3: 4
t.dispSides()
Side 1 is 3.0
Side 2 is 5.0
Side 3 is 4.0
t.findArea()
The area of the triangle is 6.0
As we can see, despite without explicitly defining methods like inputSides()
and dispSides()
for class Triangle, we were still able to use them.
If an attribute is not found in the class itself, the search continues to the base class. This repeats recursively, if the base class is itself derived from other classes.
Method Overriding In Python
In the above example, observe how the __init__()
function was defined in the Triangle and Polygon classes. When this occurs, the derived class’s method supersedes the base class’s. This means that the Triangle’s __init__()
method is given preference over the Polygon’s init method.
Generally when overriding a base method, we tend to extend the definition rather than simply replace it. The same is being done by calling the method in base class from the one in derived class (calling Polygon.init() from init() in Triangle). Utilizing the integrated feature super
would be a better choice (). so, super()
. Polygon is similar to __init (3). It is advised to use __init__(self,3)
.
Two built-in functinos isinstance()
and issubclass()
are used to check inheritances. The function isinstance()
retursnn True
if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class object
.
1
2
3
4
5
6
7
8
9
10
11
isinstance(t, Triangle)
Output: True
isinstance(t, Polygon)
Output: True
isinstance(t, int)
Output: False
isinstance(t, object)
Output: True
Similarly, issubclass()
is used to check for class inheritance.
1
2
3
4
5
6
7
8
issubclass(Polygon, Triangle)
Output: False
issubclass(Triangle, Polygon)
Output: True
issubclass(bool, int)
Output: True
Python Multiple Inheritance
In Python, a class can be derived from more than one base class. This is caleed Multiple Inheritance
. In Multiple Inheritance, the features of all the base classes are inherited into the derived class. The syntax for multiple inheritance is similar to single inheritance.
1
2
3
4
5
6
7
8
class Base1:
pass
class Base2:
pass
class MultiDerived(Base1, Base2):
pass
Here, the MultiDerived
class is derived from Base1
and Base2
classes which means it inherits attributes and featuers from both of the base classes.
Python Multilevel Inheritance
In Python, we can also inherit from a derived class, it is called Multilevel Inheritance
. In such type of inheritance, featuers of the base class as well as the dervied classes are inherited into the new derived class. Example:
1
2
3
4
5
6
7
8
class Base:
pass
class Derived1(Base):
pass
class Derived2(Derived1):
pass
Here, the Derived1
class is derived from the Base
class, and the Derived2
class is derived from the Derived1
class.
Method Resolution Order In Python
Every class in Python is derived from the object
class. It is Python’s most fundamental type. Since all objects
are instances of the object class, all other classes built-in or user-defined—are technically derived classes.
1
2
3
4
5
6
7
8
print(issubclass(list, object))
Output: True
print(isinstance(5.5, object))
Output: True
print(isinstance('Hello', object))
Output: True
In the case of multiple inheritance, the current class is first checked for any specified attribute. If not found, the search continues into parent classes in depth-first,left-right fashion without searching the same class twice.
Therefore, the search order in the MultiDerived class example above is [MultiDerived
, Base1
, Base2
, object
]. The criteria used to determine this order are known as Method Resolution Order
, and this order is also referred to as linearization of MultiDerived class (MRO).
Both local precedence ordering and monotonicity must be prevented by MRO. It guarantees that a class will always appear before of its parents. The order is the same for tuples of base classes when there are many parents.
MRO of a class can be viewed as the __mro__
attribute or the mro()
method. The former returns a tuple while the latter returns a list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MultiDerived.__mro__
(
<class '__main__.MultiDerived'>,
<class '__main__.Base1'>,
<class '__main__.Base2'>,
<class 'object'>
)
MultiDerived.mro()
[
<class '__main__.MultiDerived'>,
<class '__main__.Base1'>,
<class '__main__.Base2'>,
<class 'object'>
]
Here is a little more complex multiple inheritance example and its visualization along with the MRO.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Demonstration of MRO
class X:
pass
class Y:
pass
class Z:
pass
class A(X,Y):
pass
class B(Y,Z):
pass
class M(B,A,Z):
pass
# Output:
# [
# <class '__main__.M'>, <class '__main__.B'>,
# <class '__main__.A'>, <class '__main__.X'>,
# <class '__main__.Y'>, <class '__main__.Z'>,
# ]
print(M.mro())
Output:
[
<class '__main__.M'>,
<class '__main__.B'>,
<class '__main__.A'>,
<class '__main__.X'>,
<class '__main__.Y'>,
<class '__main__.Z'>,
<class 'object'>
]