UNIT IV
An Exception is an Unexpected Event, which occurs during the execution of the
program. It is also known as a run time error.
Python Exception Handling allows a program to gracefully handle unexpected
events (like invalid input or missing files) without crashing. Instead of terminating
abruptly, Python lets you detect the problem, respond to it, and continue execution
when possible. Exception handling teaches
“If this problem happens, do this instead.”
Common built-in exceptions
ValueError
TypeError
IndexError
KeyError
ZeroDivisionError
FileNotFoundError
Syntax and Usage
Python provides four main keywords for handling exceptions: try, except, else and
finally each plays a unique role
try:
# Some Code....
except:
# optional block
# Handling of exception (if required)
else:
# execute if no exception
finally:
# Some code .....(always executed)
try: Runs the risky code that might cause an error.
except: Catches and handles the error if one occurs.
else: Executes only if no exception occurs in try.
finally: Runs regardless of what happens useful for cleanup tasks like
closing files.
Working of 'try' and 'except'
First try clause is executed i.e. the code between try and except clause.
If there is no exception, then only try clause will run, except clause will not
get executed.
If any exception occurs, the try clause will be skipped and except clause
will run.
A try statement can have more than one except clause.
Example:
try:
numerator = 10
denominator = 0
result = numerator / denominator
print(result)
except ZeroDivisionError:
print("Error: Denominator cannot be 0.")
finally:
print("Execution completed.")
Output
Can't be divided by zero!
Explanation:
Dividing a number by 0 raises a ZeroDivisionError. The try block contains
code that may fail and except block catches the error, printing a safe message
instead of stopping the program.
Multiple Exceptions in Python
Python can handle multiple exceptions
try:
x = int("Apple")
inv = 1 / x
except ValueError:
print("Not Valid!")
except ZeroDivisionError:
Python finally Keyword
Python provides a keyword finally, which is always executed after try and except
blocks. The finally block always executes after normal termination of try block or
after try block terminates due to some exception.
def divide(x, y):
try:
result = x // y
except ZeroDivisionError:
print("You are dividing by zero ")
else:
print(" Your answer is :", result)
finally:
print('This is always executed')
divide(3, 2)
divide(3, 0)
Output
Your answer is : 1
This is always executed
You are dividing by zero
This is always executed
Advantages
Improved reliability
Cleaner code
Helpful debugging.
Disadvantages
Performance overhead: Handling exceptions is slower than simple
condition checks.
Added complexity: Multiple exception types may complicate code.
Security risks: Poorly handled exceptions might leak sensitive details
CUSTOM EXCEPTIONS
A custom exception is a user-defined exception created by inheriting from
the built-in Exception class.
We create custom exceptions when:
Built-in exceptions are not sufficient
We want meaningful error messages
We want better program structure
Creating a Simple Custom Exception
syntax
class CustomError(Exception):
...
pass
try:
...
except CustomError:
...
Here, CustomError is a user-defined error which inherits from the Exception class.
Example:
class InvalidAgeException(Exception):
"Raised when the input value is less than 18"
pass
number = 18
try:
x = int(input("Enter a number: "))
if x < number:
raise InvalidAgeException
else:
print("Eligible to Vote")
except InvalidAgeException:
print("Exception occurred: Invalid Age")
Output
If the user input input_num is greater than 18,
Enter a number: 45
Eligible to Vote
If the user input input_num is smaller than 18,
Enter a number: 14
Exception occurred: Invalid Age
In the above example, we have defined the custom exception InvalidAgeException
by creating a new class that is derived from the built-in Exception class.
Raising a Custom Exception
To raise a custom exception, use the raise keyword followed by an instance of your
custom exception.
Advantages of custom Exception
Clear Error Identification
Improves Code Readability
Better Error Handling
Suitable for Large Applications
ASSERT STATEMENT
The assert statement in Python is a debugging tool used to verify conditions
that should always be true during development. If the condition evaluates to False,
the program immediately halts and raises an AssertionError exception.
Syntax
The assert statement has two forms:
[Link] condition: Checks the condition. If False, it raises an AssertionError with
no message. Assert statement has a condition and if the condition is not satisfied
the program will stop and give AssertionError.
[Link] condition, message: Checks the condition. If False, it raises an
AssertionError and displays the custom error message.
Assert keyword without an error message
a=4
b=0
print("The value of a / b is : ")
assert b != 0
print(a / b)
Output:
The value of a / b is :
AssertionError
Traceback (most recent call last)
Input In [19], in <cell line: 10>()
9 print("The value of a / b is : ")
10 assert b != 0
11 print(a / b)
Assert keyword with an error message
a=4
b=0
print("The value of a / b is : ")
assert b != 0, "Zero Division Error"
print(a / b)
Output:
AssertionError: Zero Division Error
Assert with boolean Condition
The assert statement checks whether the boolean condition x < y is true. If the
assertion fails, it raises an AssertionError. If the assertion passes, the program
continues and prints the values of x and y.
x = 10
y = 20
assert x < y
print("x =", x)
print("y =", y)
Output:
x = 10
y = 20
Assert Type of Variable in Python
The assert statements check whether the types of the variables a and b are str and
int, respectively. If any of the assertions fail, it raises an AssertionError. If both
assertions pass, the program continues and prints the values of a and b.
a = "hello"
b = 42
assert type(a) == str
assert type(b) == int
print("a =", a)
print("b =", b)
Output:
a = hello
b = 42
The main difference is that assertions are for catching internal programming
errors during development, while exceptions are for handling recoverable
runtime errors and unexpected external conditions that can occur even in
correct code.
CLASS
Python’s object-oriented programming (OOP) .
A class in Python is a user-defined template for creating objects. It
bundles data and functions together, making it easier to manage and
use them.
Creating Class:
[Link] are created using class keyword. Attributes are variables defined
inside class and represent properties of the class. Attributes can be accessed using
dot . operator.
2. We define a class using the class keyword and initialize its attributes with
the special __init__() method. It is used to initialize the attributes of the object
with the values provided at the time of object creation. The self parameter
represents the instance of the class.
Syntax
class MyClass:
def __init__(self, attribute1, attribute2):
self.attribute1 = attribute1
self.attribute2 = attribute2
class: The keyword to start a class definition in Python.
MyClass: The name of the class, typically with every word in capitals,
including the first word.
__init__: The constructor (or init) method to set default values for the new
instance.
self: A parameter to reference the new instance.
attribute1, attribute2: Attributes of the class, often referred to as instance
variables.
Example:
import math
class Circle:
def __init__(self, radius):
[Link] = radius
def area(self):
return pi * [Link] ** 2
Creating Objects from a Class
An object is an instance of a class.
Syntax
object_name = ClassName(arguments)
x=Circle(10)
ClassName() calls the constructor (__init__() method).
Example:
class Student:
def __init__(self, name, age):
[Link] = name
[Link] = age
def display(self):
print("Name:", [Link])
print("Age:", [Link])
s1 = Student("Rahul", 20)
[Link]()
Output:
Name: Rahul
Age: 20
Methods in python
Class methods define a class's behavior and allow instances of a class to
perform specific actions. We can define class methods using the def keyword
inside the class body.
Syntax
class ClassName:
def method_name(self):
# method body
pass
self -refers to the current object.
Example
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
def myfunc(self):
print("Hello my name is " , [Link])
p1 = Person("John", 36)
[Link]()
output
Hello my name is John
Types of Methods in Python
There are mainly 3 types of methods:
1️.Instance Method
2️.Class Method
3️.Static Method
Instance Method
It Works with object data (instance variables)
It Requires self
It ia one of the most commonly used method
(self refers to object, cls refers to class.)
Example
class Student:
def __init__(self, name):
[Link] = name
def display(self): # Instance method
print("Name:", [Link])
s1 = Student("Rahul")
[Link]()
Class Method
It works with class variables
It Uses @classmethod decorator
It Uses cls instead of self
Example
class Student:
school = "ABC College"
@classmethod
def get_school(cls):
print("School:", [Link])
Student.get_school()
Static Method
Does not use self or cls
Uses @staticmethod decorator
Used for utility functions
Example
class Math:
@staticmethod
def add(a, b):
return a + b
print([Link](5, 3))
SELF VARIABLE
self is a fundamental concept when working with object-oriented
programming (OOP). It represents the instance of the class being used.
Whenever we create an object from a class, self refers to the current object
instance. It binds the attributes with the given arguments.
self is used as the first parameter in instance methods to refer to the current
object.
Example
class Subject:
def __init__(self, attr1, attr2):
self.attr1 = attr1
self.attr2 = attr2
obj = Subject('Maths', 'Science')
print(obj.attr1)
print(obj.attr2)
Output
Maths
Scienc
DATA HIDING
Data hiding is also known as data encapsulation and it is the process of
hiding the implementation of specific parts of the application from the user.
Data hiding in python is a technique of preventing methods and variables of
a class from being accessed directly outside of the class in which the methods and
variables are initialized.
Syntax
The syntax for hiding data in python
__variablename
If we want to hide any variable, then we have to use the double underscore() before
the variable name. This makes the class members inaccessible and private to other
classes.
Example
class Add:
def __init__(self, a, b):
self.__a = a # private variable
self.__b = b # private variable
def sum(self):
return self.__a + self.__b
calc = Add(num1, num2)
print("Addition:", [Link]())
Advantages of Data Hiding:
1. It helps to prevent damage or misuse of volatile data by hiding it from the
public.
2. The class objects are disconnected from the irrelevant data.
3. It isolates objects as the basic concept of OOP.
4. It increases the security against hackers that are unable to access important
data.
Disadvantages of Data Hiding:
1. It enables programmers to write lengthy code to hide important data from
common clients.
2. The linkage between the visible and invisible data makes the objects work
faster, but data hiding prevents this linkage.
CONSTRUCTOR
In Python, a constructor is a special method used to initialize objects when
they are created. It sets up the initial state of an object by assigning values to its
attributes.
1. The constructor method in Python is always named __init__().
2. It is called automatically when you create an object.
3. It can take parameters to initialize attributes.
4. self refers to the current object being created.
Syntax
class ClassName:
def __init__(self, param1, param2):
self.attribute1 = param1
self.attribute2 = param2
Creating a constructor in Python
The __init__() method acts as a constructor. It needs a mandatory argument named
self, which is the reference to the object.
def __init__(self, parameters):
The __init__() method as well as any instance method in a class has a mandatory
parameter, self. However, you can give any name to the first parameter, not
necessarily self.
Types of Constructor in Python
Python has two types of constructor −
Default Constructor
Parameterized Constructor
1. Default Constructor
A default constructor does not take any parameters other than self. It
initializes the object with default attribute values.
Example
class Car:
def __init__(self):
#Initialize the Car with default attributes
[Link] = "Toyota"
[Link] = "Corolla"
[Link] = 2020
# Creating an instance using the default constructor
car = Car()
print([Link])
print([Link])
print([Link])
Output
Toyota
Corolla
2020
2. Parameterized Constructor
A parameterized constructor accepts arguments to initialize the object's
attributes with specific values.
Example
class Car:
def __init__(self, make, model, year):
#Initialize the Car with specific attributes.
[Link] = make
[Link] = model
[Link] = year
# Creating an instance using the parameterized constructor
car = Car("Honda", "Civic", 2022)
print([Link])
print([Link])
print([Link])
Output
Honda
Civic
2022
METHOD OVERLOADING IN PYTHON
In many programming languages like C++ or Java, we can define multiple
methods with the same name but different parameter lists. This concept is
called method overloading.
Python does not support method overloading by default. If you define multiple
methods with the same name, only the latest definition will be used.
Example
def product(a, b):
p=a*b
print(p)
def product(a, b, c):
p = a * b*c
print(p)
product(4, 5, 5)
Output
100
Explanation:
Python only recognizes the latest definition of product().
The earlier definition product(a, b) gets overwritten.
If you call product(4, 5), it will raise an error because the latest version
expects 3 arguments.
However, Python provides several ways to achieve similar [Link]
achieves similar behavior using
[Link] arguments
[Link]-length arguments (*args / **kwargs).
Using Default Arguments (None as default value)
def add(a=None, b=None):
if a is not None and b is None:
print(a)
else:
print(a + b)
add(2, 3)
add(2)
Output
Explanation:
The first parameter of "add" method is set to None. This will give us the option to
call it with or without a parameter.
When we pass arguments to the add method:
Conditional statements check how many arguments were provided.
If only a is provided, it prints a.
If both a and b are provided, it prints their sum.
This approach works for a limited number of overloads but can become
messy with more parameters.
Using Variable Arguments (*args)
In this approach, we use variable-length arguments to accept any number of
arguments and handle them inside the function.
Example
class Calculator:
def add(self, *args):
calc = Calculator()
print([Link](2, 3)) # Output: 5
print([Link](2, 3, 4, 5)) # Output: 14
OPERATOR OVERLOADING
Python allows operator overloading through special methods (also called
magic methods) that start and end with double underscores (__).
Operator overloading in Python allows same operator to work in different
ways depending on data type.
Python built-in data types allow + operator can add numbers, join strings or
merge lists and * operator can be used to repeat instances of a string.
Python also allows to do operator overloading for user defined classes by
writing methods like __add__(), __mul__(), __sub__(),, __lt__(), __gt__()
and __eq__() to make objects work for operators like +, *, -, <, > and ==
respectively.
+ operator -> calls __add__(self, other)
- operator -> calls __sub__(self, other)
== operator -> calls __eq__(self, other)
Example
# + operator for integers
print(1 + 2)
# + operator for strings (concatenation)
print("Good" + "Morning")
# * operator for numbers
print(3 * 4)
# * operator for strings (repetition)
print("Geeks" * 4)
Output
3
GoodMorning
12
GoodGoodGoodGood
Overloading + operator
class A:
def __init__(self, a):
self.a = a
# define behavior of +
def __add__(self, o):
return self.a + o.a
ob1 = A(1)
ob2 = A(2)
ob3 = A("Good”)
ob4 = A("Morning")
print(ob1 + ob2) # integer addition
print(ob3 + ob4) # string concatenation
# actual working (internally)
print(A.__add__(ob1, ob2))
print(ob1.__add__(ob2))
Explanation:
ob1 + ob2 automatically calls ob1.__add__(ob2).
Python translates it into A.__add__(ob1, ob2).
first operand (ob1) becomes self and second operand (ob2) becomes other.
Overloading Comparison Operators
Operators like >, <, and == can also be overloaded.
Example
class A:
def __init__(self, a):
self.a = a
def __gt__(self, other):
return self.a > other.a
ob1 = A(2)
ob2 = A(3)
if ob1 > ob2:
print("ob1 is greater than ob2")
else:
print("ob2 is greater than ob1")
Output
ob2 is greater than ob1
INHERITANCE
Inheritance is a fundamental concept in object-oriented programming (OOP)
that allows a class (called a child or derived class) to inherit attributes and methods
from another class (called a parent or base class).
Example:
Here, we create a parent class Animal that has a method info(). Then we
create a child classes Dog that inherit from Animal and add their own behavior.
Syntax of Inheritance
class ParentClass:
# parent class code
class ChildClass(ParentClass):
# child class code
Types of Python Inheritance
Inheritance be used in different ways depending on how many parent and child
classes are involved.
1. Single Inheritance
In single inheritance, a child class inherits from just one parent class.
class Animal:
def speak(self):
print("Animal makes sound")
class Dog(Animal):
def bark(self):
print("Dog barks")
d = Dog()
[Link]() # Inherited method
[Link]()
Output:
Animal makes sound
Dog barks
2. Multiple Inheritance
In multiple inheritance, a child class can inherit from more than one parent
class.
class Father:
def skills(self):
print("Gardening")
class Mother:
def talent(self):
print("Cooking")
class Child(Father, Mother):
pass
c = Child()
[Link]()
[Link]()
3. Multilevel Inheritance
In multilevel inheritance, a class is derived from another derived class (like a
chain).
class Grandparent:
def house(self):
print("Owns house")
class Parent(Grandparent):
pass
class Child(Parent):
pass
c = Child()
[Link]()
4. Hierarchical Inheritance
In hierarchical inheritance, multiple child classes inherit from the same parent
class.
class Parent:
def property(self):
print("Parent Property")
class Child1(Parent):
pass
class Child2(Parent):
pass
5. Hybrid Inheritance
Hybrid inheritance is a combination of more than one type of inheritance.