In this chapter, we are going to learn about how to implement the advance concept of object oriented programming in Python. We are going to cover - Inheritance, Multiple Inheritance, Method Overloading, Method Overriding and making variables private. Please note that this chapter is in continuation to previous chapter.
Inheritance in Python
In object oriented programming, Inheritance is a way to reuse data members and function members of a class (Parent class or Base class) into another class (Child class or Derived class). Its a real world concept where a Child gets some common properties from its Parent.
To inherit a parent class we use the below format.
class ChildClassName(ParentClassName):
<statement-1>
.
.
.
<statement-N>
Lets consider an example
class Logger:
def __init__(self, name):
self.name = name
def write(self, message):
print(f"\n[Writing to {self.name}] - {message}")
class FileLogger(Logger):
def __init__(self, name):
Logger.__init__(self, name)
class DBLogger(Logger):
def __init__(self, name):
Logger.__init__(self, name)
logMessage = "This is a message"
fileLogger = FileLogger("File")
dbLogger = DBLogger("Database")
fileLogger.write(logMessage)
dbLogger.write(logMessage)
Here, in the example above, we have a parent class Logger and two child classes FileLogger and DBLogger. The parent class Logger contains a method write which is inherited in the child classes. Then we have created instance of both child classes. Since write method is inherited from parent class, so it is available to child classes and we can call this method by the child class instances (fileLogger and dbLogger).
The child constructor must call the parent constructor (In above example like Logger.__init__(self, name)) to initialize the parent members which the parent constructor is expecting to be initialized.
Multiple Inheritance
Just like inheritance, if a child class is inherited from two or more parent class, then we call this inheritance as multiple inheritance. All the public variables and methods of all parent class are available in the child class. Here is an example below.
class Employee:
def __init__(self, empCode):
self.empCode = empCode
def isValidEmployee(self):
return str(self.empCode).startswith("ES-")
class Student:
def __init__(self, idNumber):
self.idNumber = idNumber
def isValidStudent(self):
return str(self.idNumber).startswith("SA-")
class EmployeeAndStudent(Employee, Student):
def __init__(self, empCode, idNumber):
Employee.__init__(self, empCode)
Student.__init__(self, idNumber)
def isValid(self):
return Employee.isValidEmployee(self) and Student.isValidStudent(self)
alice = EmployeeAndStudent("ES-004", "SA-009")
if(alice.isValid()):
print("Alice is a valid student and employee")
else:
print("Alice is not a valid student or employee")
bob = EmployeeAndStudent("ES-004", "SB-009")
if(bob.isValid()):
print("Bob is a valid student and employee")
else:
print("Bob is not a valid student or employee")
In the above example, the child class EmployeeAndStudent is inherited by two parent classes (Employee and Student). Hence the isValidEmployee() and isValidStudent() are available in the child class. We are calling both methods inside the child class method isValid().
Method Overloading
Method overloading in Python is achieved by passing default values in the in the argument list. Passing default values in arguments was already covered in the chapter - Functions and lambda expressions.
Lets see an example how we can implemented method overloading with this feature.
class Box:
def __init__(self, name):
self.name = name
def setDimentions(self, width = 10, length = 10, height = 10):
self.width = width
self.length = length
self.height = height
def printBox(self):
print(f"{self.name} dimensions : Width - {self.width}, Length - {self.length}, Height - {self.height}")
box1 = Box("Box 1")
box1.setDimentions()
box2 = Box("Box 2")
box2.setDimentions(20)
box3 = Box("Box 3")
box3.setDimentions(20, 30)
box4 = Box("Box 4")
box4.setDimentions(20, 30, 40)
box5 = Box("Box 5")
box5.setDimentions(length=25)
box1.printBox()
box2.printBox()
box3.printBox()
box4.printBox()
box5.printBox()
Method Overriding
In object oriented programming, method overriding is a way in which a child class provides its own method definition instead of using the parent's method definition. This means that child class has overridden the method of the base class.
Lets see this by extending our previous Logger class example.
class Logger:
def __init__(self, name):
self.name = name
def write(self, message):
print(f"\n[Writing to {self.name}] - {message}")
class FileLogger(Logger):
def __init__(self, name, filePath):
self.name = name
self.filePath = filePath
def write(self, message):
print(f"\n[Writing to {self.name} -> {self.filePath}] - {message}")
class DBLogger(Logger):
def __init__(self, name):
self.name = name
logMessage = "This is a message"
fileLogger = FileLogger("File", "/logs/service.logs")
dbLogger = DBLogger("Database")
fileLogger.write(logMessage)
dbLogger.write(logMessage)
In the above example, we have overridden the base class method write into one child class FileLogger. Hence, when calling the write method on FileLogger class instance, the FileLogger write method will be invoked instead of the Logger write method.
Private data members
This is a fundamental requirement of any class that it should not expose its data members to be manipulated directly outside the class and therefore there should be some way to hide the data members or making then private. In python we can make a class or instance member private by prefixing __ (double underscore) before a variable name. Lets see this with an example.
class Employee:
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
alice = Employee("Alice")
name = alice.getName()
print(name)
In above example, if we try to access alice.__name then Python interpreter will give the error - AttributeError: 'Employee' object has no attribute '__name'
Python jupyter notebook for code examples
Access Jupyter notebook for this chapter here:
Jupyter Notebook - OOP inheritance, overloading and overriding