06 OOP - 05 Inheritance in Python
Inheritance is a mechanism in object-oriented programming where one class (the subclass) inherits the attributes and methods of another class (the superclass). This allows the subclass to reuse code and add its own unique features or modify inherited behavior.
class B(A):
pass
class Subclass(BaseClass):
pass
In this case, class B
inherits the properties and methods of class A
. The subclass B
can access the members of both class A
and class B
.
For example, if a Student
class is created using a Teacher
class:
class Student(Teacher):
pass
Here, the Student
class inherits all properties and methods of the Teacher
class. When an object of the Student
class is created, it contains a copy of the Teacher
class within it, establishing a relationship between the two.
Overriding Superclass Constructors and Methods
When a subclass is created, it automatically has access to the constructor of the superclass. Any variables or values defined in the superclass constructor are also available in the subclass. However, when a constructor is defined in the subclass, the superclass constructor is no longer available. Only the subclass constructor is accessible from the subclass object. This concept is known as Constructor Overriding.
Similarly, if a method is defined in the subclass with the same name as a method in the superclass, it will override the superclass method. This is known as Method Overriding.
Example of Constructor and Method Overriding:
class Father:
def __init__(self):
self.property = 80000.00
def display_property(self):
print(f"Father's Property = {self.property}")
class Son(Father):
def __init__(self):
self.property = 20000.00
def display_property(self):
print(f"Son's Property = {self.property}")
# Creating an instance of the Son class
s = Son()
s.display_property()
# Output: Son's Property = 20000.00
In this example, the subclass Son
overrides both the constructor and the method from the Father
class. Overriding should be done when we want to modify the existing behavior of a constructor or method in the subclass.
Using ‘super()’ Method
When a constructor is written in the subclass, the constructor of the superclass is not automatically available. The built-in super()
function provides a way to call the superclass constructor or methods from the subclass.
Syntax:
super().__init__() # Calls the superclass constructor
super().__init__(arguments) # Passing arguments to the superclass constructor
super().method() # Calls the superclass method
When the superclass constructor has parameters, the subclass must define a constructor with parameters as well and use super()
to call the superclass constructor from within the subclass constructor to pass the parameter.
# sub class constructor
def __init__(self, prop1 = 0, prop2 = 0):
super().__init__(prop2) # Sending to super class constructor
self.property = prop2 # storing in sub class
So two parameters need to be passed here while creating the sub class.
Example of passing argument through super()
class Father:
def __init__(self, property=0):
self.property = property
def display_property(self):
print(f"Father's Property = {self.property}")
class Son(Father):
def __init__(self, property1=0, property=0):
super().__init__(property) # Calling superclass constructor
self.property1 = property1
def display_property(self):
print(f"Son's Property = {self.property1 + self.property}")
# Creating an instance of Son with two parameters
s = Son(20000, 80000)
s.display_property()
# Output: Son's Property = 100000
In this example, we use super()
to call the super class constructor and override methods in a subclass:
class Square:
def __init__(self, x):
self.x = x
def area(self):
print(f"Area of square = {self.x * self.x}")
class Rectangle(Square):
def __init__(self, x, y):
super().__init__(x) # Calls the constructor of Square
self.y = y
def area(self):
super().area() # Calls the area method of Square
print(f"Area of rectangle = {self.x * self.y}")
# Create an instance of Rectangle and calculate areas
a, b = [float(x) for x in input("Enter two measures: ").split()]
r = Rectangle(a, b)
r.area()
# Example input: 10 5.5
# Output:
# Area of square = 100.0
# Area of rectangle = 55.0
Types of Inheritance in Python
Single Inheritance
Single inheritance refers to the process of deriving one or more subclasses from a single base class. A subclass inherits the properties and behaviors (attributes and methods) of only one superclass.
class Bank:
cash = 1000000 # class variable
@classmethod
def available_cash(cls):
print(cls.cash)
class AbBank(Bank):
pass
class StateBank(Bank):
cash = 200000 # overriding the base class variable
@classmethod
def available_cash(cls):
print(cls.cash + Bank.cash)
# Creating objects of subclasses
a = AbBank()
a.available_cash() # Output: 1000000
s = StateBank()
s.available_cash() # Output: 1200000
Bank
class has a class variablecash
and a class methodavailable_cash()
.StateBank
overrides thecash
variable and modifies the behavior of theavailable_cash()
method to include its class variable also.AbBank
does not overridecash
, so it uses the class variable fromBank
.
In cls.cash + Bank.cash
, cls.cash
refers to the current class’s cash
variable, and Bank.cash
refers to the cash
variable in the superclass.
Multiple Inheritance
Multiple inheritance occurs when a subclass inherits from more than one base class. In Python, all base classes are available to the subclass, and the subclass can have its own members as well.
class Subclass(Baseclass1, Baseclass2, ...):
class C(A, B):
class A:
def __init__(self):
self.a = 'a'
print(self.a)
class B:
def __init__(self):
self.b = 'b'
print(self.b)
class C(A, B): # Inheriting from both A and B
def __init__(self):
self.c = 'c'
print(self.c)
super().__init__() # Calls constructor of the superclass
# Creating an object of class C
opp = C()
# Output:
# c
# a
- Class
C
inherits from bothA
andB
. super().__init__()
ensures the constructor of the superclass (A
in this case) is called. As a result, it first printsc
(fromC
), thena
(fromA
).
While using super()
function in sub class constructor to call the constructors of super classes returns only one from the two, (the one at the left hand side of). If left one doesn’t have a constructor then the one on the right side is called. This means C cannot access the instance variables of both the super classes.
If C has to access instances of all base classes, then all the Base classes also need to use super().__init__()
class A:
def __init__(self):
self.a = 'a'
print(self.a)
super().__init__()
class B:
def __init__(self):
self.b = 'b'
print(self.b)
super().__init__()
class C(A, B):
def __init__(self):
self.c = 'c'
print(self.c)
super().__init__()
opp = C()
# c
# a
# b
When constructor of C is called it prints C and then the super().__init__()
is called and it moves to constructor of A and prints A, but constructor of A also has a super().init()
which will try to execute the constructor of object class and since it does not have any, the search continues on the right hand side where B is printed and move on to object class from its constructor.
Searching in this manner is called as Method Resolution Order(MRO)
Method Resolution Order (MRO)
In the case of multiple inheritance, Python uses the Method Resolution Order (MRO) to determine the order in which constructors and methods are called.
MRO dictates the order in which base classes are searched for a method or attribute. In multiple inheritance, Python follows the left-to-right, depth-first search principle, and each class is visited only once.
Order of search:
- First, search the subclass.
- If not found, search the base classes from left to right (in the order the classes are inherited).
- Python does not visit the same class twice.
In the case of multiple inheritance, MRO can be viewed using the mro()
method or __mro__
attribute.
print(C.mro()) # Prints the MRO for class C