OO Python-Module 2 (1)
OO Python-Module 2 (1)
Python
1|Page
Module 2: Python Inheritance, Polymorphism & Exception Handling
Types on inheritance, Single Inheritance, Multiple Inheritance, Method overloading and Method Overriding,
Method Resolution Order(MRO), Duck Typing Philosophy of Python Abstract method and Abstract Class,
Interfaces in Python - Compile time errors, Runtime Errors, Logical Errors, Exception, Exception handling,
Types of Exception.
Inheritance permits new classes to inherit features from existing classes, decreasing redundancy and
reusing code. Polymorphism lets objects of diverse types share the same interface, empowering methods to
be executed unexpectedly based on the object type. This decreases the code required and streamlines
upkeep.
Inheritance could be a feature of object-oriented programming that permits one class to acquire
characteristics from another class. In other words, inheritance permits a class to be characterized in terms of
another class, which makes it simpler to make and keep up an application.
Polymorphism allows objects of different types to be treated similarly. In other words, polymorphism
allows objects to be treated as a single type of object, even if they are of different types. This means that a
single set of code can handle any object, even if the objects are of different types.
Inheritance:
Polymorphism:
It permits for the execution of dynamic dispatch and the implementation of interfaces.
It reduces the number of lines of code and makes it simpler to maintain.
It permits for the execution of more generic algorithms.
It permits the execution of more adaptable programs.
18 | P a g e
Types of Inheritance in Python
In object-oriented terminology when class X extend class Y, then Y is called super class or base class
and X is called subclass or derived class. One more point to note that only data fields and method which are
not private are accessible by child classes, private data fields and methods are accessible only inside the
class.
class SubClass(SuperClass):
# data fields
# instance methods
class Vehicle:
class Car(Vehicle):
def getDescription(self):
return self.getName() + self.__model + " in " + self.getColor() + " color"
# in method getDescrition we are able to call getName(), getColor() because they are
# accessible to child class through inheritance
19 | P a g e
c = Car("Ford Mustang", "red", "GT350")
print(c.getDescription())
print(c.getName()) # car has no method getName() but it is accessible via class Vehicle
Output:
Suppose you need to call a method called get_information() in the base class from child class , you
can do so using the following code.
super().get_information()
Similarly, you can call base class constructor from child class constructor using the following code.
super().__init__()
Multiple inheritance
Unlike languages like Java and C#, python allows multiple inheritance i.e you can inherit from
multiple classes at the same time like this,
class MySuperClass1():
def method_super1(self):
print("method_super1 method called")
class MySuperClass2():
def method_super2(self):
print("method_super2 method called")
def child_method(self):
print("child method")
20 | P a g e
c = ChildClass()
c.method_super1()
c.method_super2()
Expected Output:
As you can see becuase ChildClass inherited MySuperClass1, MySuperClass2, object of ChildClass is now
able to access method_super1() and method_super2().
Overriding methods
To override a method in the base class, sub class needs to define a method of same signature. (i.e
same method name and same number of parameters as method in base class).
class A():
def __init__(self):
self.__x = 1
def m1(self):
print("m1 from A")
class B(A):
def __init__(self):
self.__y = 1
def m1(self):
print("m1 from B")
c = B()
c.m1()
Expected Output:
m1 from B
Here we are overriding m1() method from the base class. Try commenting m1() method in B class and
now m1() method from Base class i.e class A will run.
21 | P a g e
Expected Output:
m1 from A
isinstance() function
The isinstance() function is used to determine whether the object is an instance of the class or not.
Overloading and Overriding in Python are the two main object-oriented concepts that allow
programmers to write methods that can process a variety of different types of functionalities with the same
name. This helps us to implement Polymorphism and achieve consistency in our code. Let's look at an
example to understand the concept of overloading and overriding:
Consider a scenario where you must select arrows for shooting practice in archery. Here, the concept of
overloading and overriding can be depicted as follows:
overloading-and-overriding-in-python
Consider a case where you have 3 similar-looking arrows with different functionalities or properties.
Now, depending on the condition of shooting, the arrow is selected. This is known as overloading. Hence,
22 | P a g e
overloading implies using numerous methods with the same name but accepting a different number of
arguments.
Now, consider another case where your Dad has an arrow. You have access to this arrow, as you
inherited it. But, you also bought the same type of arrow for yourself. Now, while shooting if you are
choosing your arrow over your Dad's arrow, then it is known as overriding.
Now, let's look at overloading and overriding in Python from a technical point of view.
Method Overloading in Python is a type of Compile-time Polymorphism using which we can define
two or more methods in the same class with the same name but with a different parameter list.
Code:
def sum_number(*args):
result = 0
result += num
# Output
# Driver Code
if(__name__ == "__main__"):
sum_number(10)
sum_number(30, 2)
sum_number(1, 2, 3, 4, 5)
23 | P a g e
Output:
Method Overriding is a type of Run-time Polymorphism. A child class method overrides (or provides
its implementation) the parent class method of the same name, parameters, and return type. It is used to
over-write (redefine) a parent class method in the derived class. Let's look at an example:
Code:
# Parent Class
class A:
def first(self):
def second(self):
# Derived Class
class B(A):
# Overriden Function
def first(self):
def display(self):
# Driver Code
if(__name__ == "__main__"):
child_obj = B()
24 | P a g e
# Calling the overridden method
print("Method Overriding\n")
child_obj.first()
A().first()
Output:
Method Overriding
Quiz Pop
new tag
Quiz Type
SCQ
100
Success Rate:
35%
Methods must have the same name Methods must have the same name
and different parameters. and same parameters.
Can occur within the same class or Occurs in a subclass that inherits
its subclasses. from a superclass.
25 | P a g e
Method Overloading Method Overriding
In this tutorial, we will learn about method resolution order, which is also known as the MRO. It is an
essential concept of Python inheritance.
Method resolution order describes the search path of the class which Python uses to get the appropriate
method in classes that contain the multi-inheritance.
Introduction
As we know that, a class that being inherited is called the Subclass or Parent class, while the class
that inherits is known as a child class or subclass. In the multi-inheritance, a class can consist of many
functions, so the method resolution order technique is used to search the order in which the base class is
executed.
In simple words - "The method or attributes is explored in the current class, if the method is not present in
the current class, the search moves to the parent classes, and so on". This is an example of a depth-first
search.
It plays an essential role in multiple inheritance where the same method can be found in the multiple
superclasses.
26 | P a g e
To understand it in a better way, the following example is given here.
Example -
class A:
def myname(self):
class B(A):
def myname(self):
class C(A):
def myname(self):
c = C()
print(c.myname())
Output:
I am a class C
Explanation -
There is a multiple inheritance in the above code. We have defined three-class called A, B, and C,
and these classes have the same name method called myname(). We created an object class C. The object
invoked class C, not the class, while class C inherited the class A method.
The order is followed in the above code is class B - > class A. This technique is known as MRO
(method resolution order).
Example -
class A:
def myname(self):
class B(A):
def myname(self):
class C(A):
def myname(self):
27 | P a g e
# classes ordering
pass
d = D()
d.myname()
Output:
I am a class B
Explanation -
In the above code, we have created another D class without defining class attributes that inherited
B and C class. When we invoked the method myname(), it goes to class D and searches for the myname()
function. But class D doesn't have any declaration. Hence, the search transfers to class B, gets the
myname() function, and returns the result. The search will take place as follows.
Here, we are suggesting you remove the class B method and check what happens. By doing this, you will
get an idea of how the method resolution works.
In this tutorial, we will learn about duck typing. It is a popular term in Python, and it comes from
saying, "If it walks like duck, swims like duck, looks like a duck, then it probably should be a duck."
The above statement gives an idea to identify a duck. Here we don't need to have a genomic sequence of
the duck. We draw our conclusion by its behavior and external appearances.
Python follows the EAFP (Easier to Ask Forgiveness than Permission) rather than the LBLY (Look Before You
Leap) philosophy. The EAFP is somewhat linked to the "duck typing" style.
Play
Next
Mute
Current Time
0:00
Duration
18:10
Fullscreen
28 | P a g e
Play Video
Advertisement
The main reason for using duck typing is to provide support for dynamic typing in Python
programming. In Python, we don't need to specify the variable's data type and we can reassign the
different data type values to same variable in further code. Let's see the following example.
Example -
x = 12000
print(type(x))
x = 'Dynamic Typing'
print(type(x))
x = [1, 2, 3, 4]
print(type(x))
Output:
<class 'int'>
<class 'str'>
<class 'list'>
As we can see in the above code, we assigned an integer to a variable x, making it of the int type.
Then, we assigned a string and a list to the same variable. Python interpreter accepts the changes of data
types of the same variable. This is a dynamic typing behavior. Many other programming languages such as
Java, swift are the static type. We need to declare variable with the data types. In the below example, we
try to do the same thing using the Swift instead of Python.
Example -
var a = 10
a = 'Swift language'
Above code cannot be compiled, because we couldn't assign a string in Swift language. Because variable a
was declared as an integer.
29 | P a g e
Concept of Duck Typing
Earlier, we have discussed that Python is a dynamic typed language. However, we can use the dynamic
approach with custom data types. Let's understand the following example.
Example -
class VisualStudio:
def execute(self):
print('Compiling')
print('Running')
print('Spell Check')
print('Convention Check')
class Desktop:
ide.execute()
ide = VisualStudio()
desk = Desktop()
desk.code(ide)
Output:
Compiling
Running
Spell Check
Convention Check
In the above code, we have created a VisualStudio class that has to execute() method. In the
desktop-class, we have passed the ide as an argument in the code(). An ide is an object of VisualStudio
class. With the help of ide, we called the execute() method of VisualStudio class.
Another example.
Example - 2
class Duck:
def swim(self):
class Sparrow:
def swim(self):
30 | P a g e
class Crocodile:
def swim_walk(self):
def duck_testing(animal):
animal.swim()
duck_testing(Duck())
duck_testing(Sparrow())
duck_testing(Crocodile())
Output:
In the above code, the Duck class's instance is reflected by calling the duck_testing function. It also
happens with the Sparrow class, which implements the swim() function. But in the case of the Crocodile
class, it fails the duck testing evaluation because it doesn't implement the swim() function.
31 | P a g e
ABC works by decorating methods of the base class as an abstract and then registering concrete
classes as implementations of the abstract base. A method becomes abstract when decorated with the
keyword @abstractmethod.
Example 1:
This code defines an abstract base class called “Polygon” using the ABC (Abstract Base Class)
module in Python. The “Polygon” class has an abstract method called “noofsides” that needs to be
implemented by its subclasses.
There are four subclasses of “Polygon” defined: “Triangle,” “Pentagon,” “Hexagon,” and “Quadrilateral.”
Each of these subclasses overrides the “noofsides” method and provides its own implementation by
printing the number of sides it has.
In the driver code, instances of each subclass are created, and the “noofsides” method is called on each
instance to display the number of sides specific to that shape.
class Polygon(ABC):
@abstractmethod
def noofsides(self):
pass
class Triangle(Polygon):
def noofsides(self):
class Pentagon(Polygon):
def noofsides(self):
class Hexagon(Polygon):
def noofsides(self):
32 | P a g e
class Quadrilateral(Polygon):
def noofsides(self):
# Driver code
R = Triangle()
R.noofsides()
K = Quadrilateral()
K.noofsides()
R = Pentagon()
R.noofsides()
K = Hexagon()
K.noofsides()
Output
I have 3 sides
I have 4 sides
I have 5 sides
I have 6 sides
Example 2:
Here, This code defines an abstract base class called “Animal” using the ABC (Abstract Base Class)
module in Python. The “Animal” class has a non-abstract method called “move” that does not have any
implementation. There are four subclasses of “Animal” defined: “Human,” “Snake,” “Dog,” and “Lion.” Each
of these subclasses overrides the “move” method and provides its own implementation by printing a
specific movement characteristic.
class Animal(ABC):
def move(self):
pass
33 | P a g e
class Human(Animal):
def move(self):
class Snake(Animal):
def move(self):
class Dog(Animal):
def move(self):
class Lion(Animal):
def move(self):
# Driver code
R = Human()
R.move()
K = Snake()
K.move()
R = Dog()
R.move()
K = Lion()
K.move()
Output
I can walk and run
I can crawl
I can bark
I can roar
34 | P a g e
Concrete Methods in Abstract Base Classes
Concrete classes contain only concrete (normal) methods whereas abstract classes may contain
both concrete methods and abstract methods.
The concrete class provides an implementation of abstract methods, the abstract base class can also
provide an implementation by invoking the methods via super(). Let look over the example to invoke the
method using super():
class R(ABC):
def rk(self):
print("Abstract Base Class")
class K(R):
def rk(self):
super().rk()
print("subclass ")
# Driver code
r = K()
r.rk()
Output
Interfaces in Python
In software engineering, an interface is a software architectural pattern. It is similar to a class but its
methods just have prototype signature definition without any executable code or implementation body. The
required functionality must be implemented by the methods of any class that inherits the interface.
The method defined without any executable code is known as abstract method.
Interfaces in Python
In languages like Java and Go, there is keyword called interface which is used to define an interface. Python
doesn't have it or any similar keyword. It uses abstract base classes (in short ABC module) and
@abstractmethod decorator to create interfaces.
Key Point: In Python, abstract classes are also created using ABC module.
An abstract class and interface appear similar in Python. The only difference in two is that the
abstract class may have some non-abstract methods, while all methods in interface must be abstract, and
the implementing class must override all the abstract methods.
35 | P a g e
Rules for implementing Python Interfaces
We need to consider the following points while creating and implementing interfaces in Python −
Methods defined inside an interface must be abstract.
Creating object of an interface is not allowed.
A class implementing an interface needs to define all the methods of that interface.
In case, a class is not implementing all the methods defined inside the interface, the class must be
declared abstract.
Learn Python in-depth with real-world projects through our Python certification course. Enroll and
become a certified expert to boost your career.
Formal Interface
Informal Interface
Formal Interface
Formal interfaces in Python are implemented using abstract base class (ABC). To use this class, you need to
import it from the abc module.
Example
In this example, we are creating a formal interface with two abstract methods.
# creating interface
class demoInterface(ABC):
@abstractmethod
def method1(self):
return
@abstractmethod
def method2(self):
return
class concreteclass(demoInterface):
36 | P a g e
def method1(self):
return
def method2(self):
return
# creating instance
obj = concreteclass()
# method call
obj.method1()
obj.method2()
Output
Output:
This is method1
This is method2
Informal Interface
In Python, the informal interface refers to a class with methods that can be overridden. However, the
compiler cannot strictly enforce the implementation of all the provided methods. This type of interface works
on the principle of duck typing. It allows us to call any method on an object without checking its type, as
long as the method exists.
Example
class demoInterface:
def displayMsg(self):
pass
class newClass(demoInterface):
def displayMsg(self):
# creating instance
37 | P a g e
obj = newClass()
# method call
obj.displayMsg()
Output
This is my message
Errors are problems that occur in the program due to an illegal operation performed by the user or
by the fault of a programmer, which halts the normal flow of the program. Errors are also termed bugs or
faults. There are mainly two types of errors in python programming. Let us learn about both types of python
errors:
The three main types of errors in Python are syntax errors, runtime errors, and logical errors:
Syntax errors
Similar to grammar mistakes in English, these occur when the rules of the Python language are not
followed. The Python interpreter can identify these errors during code writing.
Runtime errors
Also known as exceptions, these occur when the code has the correct syntax but encounters a problem
while running. These errors can be caused by unexpected conditions like dividing by zero, accessing an
index out of range, or calling a function that doesn't exist.
Logical errors
These occur when the code runs without any syntax or runtime errors but produces incorrect results. These
errors are the most difficult to find because they don't produce error messages. They can be caused by
incorrect assumptions, incomplete understanding of the problem, or incorrect use of algorithms or
formulas.
Compile time is when the programming code is converted to machine code, while runtime is when the
program is running.
Syntax Errors
A syntax error is one of the most basic types of error in programming. Whenever we do not write the
proper syntax of the python programming language (or any other language) then the python interpreter
or parser throws an error known as a syntax error. The syntax error simply means that the python parser is
unable to understand a line of code.
number = 100
if number > 50
38 | P a g e
Output:
if number > 50
As we can see in the example above, the python interpretation raised a syntax error saying invalid
syntax. Since we have missed the semicolon (:) after the if statement so that's why the python interpreter
raised a syntax error.
Some of the general syntax errors can be typing errors (errors), incorrect indentation, or incorrect
arguments. In case of syntax errors, we should look at our code and try to understand the problem.
Note: In various IDEs (Integrated Development Environment), the syntax error(s) are shown by red dashed
lines.
Logical Errors
As we have seen above there are two main types of python errors. We already have studied the
syntax error, let us now learn about the logical errors in python.
Logical Errors are those errors that cannot be caught during compilation time. As we cannot check these
errors during compile time, we name them Exceptions. Since we cannot check the logical errors during
compilation time, it is difficult to find them.
There is one more name of logical error. Run time errors are those errors that cannot be caught during
compilation time. So, run-time can cause some serious issues so we should handle them effectively.
Let us now talk about some of the most common logical types of errors in python programming.
ZeroDivisionError Exception
ZeroDivisionError is raised by the Python interpreter when we try to divide any number by zero. Let us take
an example to understand the ZeroDivisionError.
number = 100
divided_by_zero = number / 0
print(divided_by_zero)
Output:
divided_by_zero = number / 0
39 | P a g e
The Indentation error is another type of error in python that can be summed up inside the syntax
error. As we know, we use indentation to define a block of code in python. So, in case of improper
indentation, the Python interpreter raises an Indentation error. Let us take an example to understand the
Indentation Error.
number = 57
if number == 57:
print(57)
Output:
print(57)
Exception Handling
Exception handling enables you handle errors gracefully and do something meaningful about it.
Like display a message to user if intended file not found. Python handles exception using try, except block.
Syntax:
try:
except <ExceptionType>:
As you can see in try block you need to write code that might throw an exception. When exception occurs
code in the try block is skipped. If there exist a matching exception type in `except clause then it's handler
is executed.
try:
f = open('somefile.txt', 'r')
print(f.read())
f.close()
except IOError:
40 | P a g e
If file don't exists then exception will be raised and the rest of the code in the try block will be skipped
When exceptions occurs, if the exception type matches exception name after except keyword, then the
code in that except clause is executed.
note:
The above code is only capable of handling IOError exception. To handle other kind of exception you need
to add more except clause.
A try statement can have more than once except clause, It can also have optional else and/or finally
statement.
try:
<body>
except <ExceptionType1>:
<handler1>
except <ExceptionTypeN>:
<handlerN>
except:
<handlerExcept>
else:
<process_else>
finally:
<process_finally>
The except clause is similar to elif. When exception occurs, it is checked to match the exception type in
except clause. If match is found then handler for the matching case is executed. Also note that in last
except clause ExceptionType is omitted. If exception does not match any exception type before the last
except clause, then the handler for last except clause is executed.
note:
Statements under the else clause run only when no exception is raised.
note:
Statements in the finally clause will run every time no matter exception occurs or not.
try:
except ZeroDivisionError:
41 | P a g e
print("Division by zero is error !!")
except SyntaxError:
except:
print("Wrong input")
else:
print("No exceptions")
finally:
note:
The eval() function lets a python program run python code within itself, eval() expects a string argument.
Raising exceptions
To raise your exceptions from your own methods you need to use raise keyword like this
def enterage(age):
if age < 0:
if age % 2 == 0:
print("age is even")
else:
print("age is odd")
try:
enterage(num)
except ValueError:
except:
print("something is wrong")
42 | P a g e
Expected Output:
age is even
Expected Output:
Now you know how to handle exception, in this section we will learn how to access exception
object in exception handler code. You can use the following code to assign exception object to a variable.
try:
As you can see you can store exception object in variable ex. Now you can use this object in exception
handler code.
try:
print("Exception:", ex)
Expected Output:
Enter a number: 34
Expected Output:
You can create a custom exception class by extending BaseException class or subclass of BaseException.
43 | P a g e
python-exception-classes.jpg
As you can see from most of the exception classes in python extends from the BaseException class. You
can derive you own exception class from BaseException class or from sublcass of BaseException like
RuntimeError.
Create a new file called NegativeAgeException.py and write the following code.
class NegativeAgeException(RuntimeError):
super().__init__()
self.age = age
Above code creates a new exception class named NegativeAgeException, which consists of only
constructor which call parent class constructor using super().__init__() and sets the age.
def enterage(age):
if age < 0:
if age % 2 == 0:
print("age is even")
else:
print("age is odd")
try:
enterage(num)
except NegativeAgeException:
except:
print("something is wrong")
44 | P a g e
Types of Exceptions:
Built-in Exceptions
The table below shows built-in exceptions that are usually raised in Python:
Exception Description
45 | P a g e
Exception Description
46 | P a g e