0% found this document useful (0 votes)
59 views20 pages

Unit 5 Pythn

Uploaded by

saikumar2632003
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
59 views20 pages

Unit 5 Pythn

Uploaded by

saikumar2632003
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

UNIT- V

OPERATOR OVERLOADING AND EXCEPTION HANDLING Operator Overloading: Introduction,


implementing operator overloading, reverse adding, overriding__getitem__() and __setitem__() methods,
overriding the in operator, overriding miscellaneous functions, overriding the _call__() method. Error and
Exception Handling: Introduction to errors and exceptions, handling exceptions, multiple except blocks,
multiple exceptions in a single block, except block without exception, the else clause, raising exceptions,
built-in and user-defined exceptions, the finally block. [10 Hours]

➢ Operator overloading (what is it)


• Python operators work for built-in classes in a predefined operational way. But the same
operator behaves differently for objects of different classes

• For example, the + operator will perform arithmetic addition on two numbers, merge two
lists, or concatenate two strings

• This feature in Python that allows the same operator to have different meanings
according to the context is called operator overloading

• Python uses special methods (magic methods or dunder methods) to implement operator
overloading. These methods are surrounded by double underscores (e.g., __add__,
__sub__).

✓ Where to Use It
• Mathematical Classes: For complex numbers, vectors, matrices, etc.
• Custom Data Structures: Such as linked lists, trees, etc.
• String and File Handling: Overloading operators like + for concatenation.

• Python magic functions used for operator overloading:


• Binary Operators:

Operator Magic Function

+ __add__(self, other)

- __sub__(self, other)

* __mul__(self, other)
/ __truediv__(self, other)

// __floordiv__(self, other)

% __mod__(self, other)

** __pow__(self, other)

>> __rshift__(self, other)

<< __lshift__(self, other)

& __and__(self, other)

| __or__(self, other)

^ __xor__(self, other)

• Comparison Operators:

Operator Magic Function

< __LT__(SELF, OTHER)

> __GT__(SELF, OTHER)

<= __LE__(SELF, OTHER)

>= __GE__(SELF, OTHER)

== __EQ__(SELF, OTHER)
!= __NE__(SELF, OTHER)

• Assignment Operators:

Operator Magic Function

-= __ISUB__(SELF, OTHER)

+= __IADD__(SELF, OTHER)

*= __IMUL__(SELF, OTHER)

/= __IDIV__(SELF, OTHER)

//= __IFLOORDIV__(SELF, OTHER)

%= __IMOD__(SELF, OTHER)

**= __IPOW__(SELF, OTHER)

>>= __IRSHIFT__(SELF, OTHER)

<<= __ILSHIFT__(SELF, OTHER)

&= __IAND__(SELF, OTHER)

|= __IOR__(SELF, OTHER)

^= __IXOR__(SELF, OTHER)

• Unary Operator:
Operator Magic Function

- __NEG__(SELF, OTHER)

+ __POS__(SELF, OTHER)

~ __INVERT__(SELF, OTHER)

✓ How to Use Operator Overloading


• Define a Class: Create a class where you want to implement operator
overloading.
• Define Special Methods: Implement the special methods corresponding to the
operators you want to overload.
Example: -
✓ Advantages

Readability: Makes the code more intuitive and readable.

Conciseness: Reduces the need for explicit method calls.

Consistency: Allows custom objects to behave like built-in types

➢ Reverse adding
Reverse adding in Python typically refers to appending elements to the beginning of a list rather
than the end.

• Using List Slicing:

You can prepend an element by creating a new list with the element followed by the original
list.

original_list = [2, 3, 4]

element_to_prepend = 1

new_list = [element_to_prepend] + original_list

print(new_list) # Output: [1, 2, 3, 4]


• Using insert Method:

The insert method can add an element at a specified position. To add to the beginning, specify
the position as 0.

original_list = [2, 3, 4]

element_to_prepend = 1

original_list.insert(0, element_to_prepend)

print(original_list) # Output: [1, 2, 3, 4]

• Using Deque:

The collections.deque class provides an efficient way to add elements to both ends of a list.

from collections import deque

original_list = deque([2, 3, 4])

element_to_prepend = 1

original_list.appendleft(element_to_prepend)

print(list(original_list)) # Output: [1, 2, 3, 4]

• Using List Reversal:

Another method is to reverse the list, append the element, and then reverse it back.

original_list = [2, 3, 4]

element_to_prepend = 1

original_list.reverse()

original_list.append(element_to_prepend)

original_list.reverse()

print(original_list) # Output: [1, 2, 3, 4]

When considering reverse adding in Python within the context of object-oriented programming
(OOP), it might refer to a more abstract concept. This could involve defining a custom behavior
for a class to handle addition operations in a specific manner. Here, "reverse adding" could be
implemented by overloading the __radd__ method, which stands for "reverse addition."
In Python, the __add__ method is used to define the behavior of the + operator. The __radd__
method is called when the left operand does not support the addition operation with the right
operand, and the right operand implements the __radd__ method.

Example: -

class MyNumber:

def __init__(self, value):

self.value = value

def __add__(self, other):

return MyNumber(self.value + other) # Handles self + other

def __radd__(self, other):

return MyNumber(self.value + other) # Handles other + self

def __repr__(self):

return f"MyNumber({self.value})"

# Example usage

a = MyNumber(10)

result1 = a + 5 # Uses __add__: MyNumber(15)

result2 = 5 + a # Uses __radd__: MyNumber(15)

print(result1) # Output: MyNumber(15)

print(result2) # Output: MyNumber(15)

Note: -

1. __add__ Method: This method is called when your custom class instance is on the left
side of the + operator.
2. __radd__ Method: This method is called when your custom class instance is on the
right side of the + operator, and the left operand does not support the operation with the
right operand.
➢ Overriding
• Multiple function with same name and same number of parameters
• If we write method in the both classes, parent class and child class then the parent
class’s method is not available to the child class.
• In this case only child class’s method is accessible which means child class’s method is
replacing parent class’s method.
• Method overriding is used when programmer want to modify the existing behavior of a
method.
➢ __getitem__() and __setitem__()
Here's a comparison table for __getitem__() and __setitem__() in Python:

Feature __getitem__() __setitem__()


Method to get an item using a Method to set an item using a
Definition
key/index. key/index.
Retrieve the value associated with Assign a value to the given
Purpose
the given key/index. key/index.
Usage Used for accessing elements. Used for modifying elements.
Invoked using the square bracket Invoked using the square bracket
Invocation
notation for accessing. notation for setting.
def __getitem__(self, key): def __setitem__(self, key,
Syntax ... value): ...
Returns the value associated with
Return Type Returns None.
the key/index.
Examples item = obj[key] obj[key] = value
Custom container types, lists, Custom container types, lists,
Common Use Cases
dictionaries. dictionaries.
Typically raises KeyError or May raise errors if key/index is
Error Handling
IndexError if key/index is invalid. invalid or assignment is not possible.
Yes, can be overridden in Yes, can be overridden in
Polymorphism
subclasses. subclasses.
Applicable to both mutable and
Mutable/Immutable Applicable to mutable types.
immutable types.
Read/Write Read-only. Write (modify/create).
dict, list, tuple (only
Example Classes dict, list.
__getitem__ for tuple).

➢ Overriding the in operator


We can override the ‘in’ operator by implementing the ‘__contains__’ method in your
class. This method is called to determine if an element is present in a container (e.g., if
‘element in container’).
class MyCollection:
def __init__(self, items):
self.items = items

def __contains__(self, item):


# Custom logic to check if item is in the collection
return item in self.items

# Usage
my_collection = MyCollection([1, 2, 3, 4, 5])
print(3 in my_collection) # Output: True
print(6 in my_collection) # Output: False

➢ overriding the _call__() method


you can override the __call__() method to make an instance of a class callable like a
regular function. This allows you to define custom behavior that gets executed when
you call an instance of your class.
class CallableExample:
def __init__(self, message):
self.message = message

def __call__(self, times):


for _ in range(times):
print(self.message)

# Usage
callable_instance = CallableExample("Hello, World!")
callable_instance(3) # Output: Hello, World!
# Hello, World!
# Hello, World!

➢ Exception Handling: Introduction to errors and exceptions


What is an Exception
An event that occurs during program execution that disrupts the normal flow of
program
It is a type of error that occurs at run time
Types of exception/Errors
• User -defined Exception
• Built in Exception
✓ Built in Exception
1. Arithmetic Error
• Base class for errors that occur for numeric calculations.
• Subclasses: ZeroDivisionError, OverflowError, FloatingPointError..
2. Zero Division Error
Raised when dividing by zero.
Example:
result = 10 / 0 # Raises ZeroDivisionError
3. Index Error
Raised when a sequence subscript is out of range.
Example:
my_list = [1, 2, 3]
print(my_list[5]) # Raises IndexError
4. Key Error
Raised when a dictionary key is not found.
Example:
my_dict = {'a': 1}
print(my_dict['b']) # Raises KeyError
5. Type Error
Raised when an operation or function is applied to an object of
inappropriate type.
Example:
result = '2' + 2 # Raises TypeError
6. Value Error
Raised when a function receives an argument of the right type but
inappropriate value.
Example:
int('abc') # Raises ValueError
7. Attribute Error
Raised when an attribute reference or assignment fails.
Example:
my_list = [1, 2, 3]
my_list.appenditem(4) # Raises AttributeError
8. Import Error
Raised when an import statement fails.
Example:
import non_existent_module # Raises ImportError
9. Module Not Found Error
Subclass of ImportError, raised when a module is not found.
Example:
import non_existent_module # Raises ModuleNotFoundError
10. File Not Found Error

Raised when a file or directory is requested but doesn't exist.


Example:
open('non_existent_file.txt') # Raises FileNotFoundError
11. IO Error
Raised when an I/O operation (e.g., opening a file) fails.
Example:
open('/root/secret.txt', 'r') # Raises IOError
12. EOF Error
Raised when the input() function hits an end-of-file condition (EOF).
Example:
while True:
try:
line = input()
except EOFError:
break
13. OS Error

Raised when a system-related operation fails.


Subclasses: FileNotFoundError, PermissionError, TimeoutError.
14. Permission Error
Raised when trying to run an operation without the adequate access rights.
Example:
open('/etc/shadow', 'r') # Raises PermissionError
15. Runtime Error
Raised when an error does not fall under any other category.
Example:
raise RuntimeError("An error occurred") # Raises RuntimeError
16. Not Implemented Error
Raised when an abstract method that needs to be implemented in a subclass is not
actually implemented.
Example:
class BaseClass:
def my_method(self):
raise NotImplementedError("This method needs to be implemented in a
subclass")
17. Stop Iteration
Raised by next() function to indicate that there are no further items.
Example:
it = iter([1, 2, 3])
next(it)
next(it)
next(it)
next(it) # Raises StopIteration
18. Assertion Error
Raised when an assert statement fails.
Example:
assert 2 + 2 == 5 # Raises AssertionError
19. Name Error
Raised when a local or global name is not found.
Example:
print(non_existent_variable) # Raises NameError
20. Unbound Local Error
Subclass of NameError, raised when a local variable is referenced before it is
assigned.
Example:
def my_function():
print(x)
x = 10
my_function() # Raises UnboundLocalError
Handling Exceptions
Python provides a way to handle exceptions using try, except, else, and finally blocks.
Basic syntax: --
try:
# Code that might raise an exception
risky_code()
except SomeException as e:
# Code that runs if an exception occurs
handle_exception(e)
else:
# Code that runs if no exception occurs
successful_execution()
finally:
# Code that always runs, regardless of exceptions
cleanup()
Explanation
• try: The block of code where exceptions might occur.
• except: The block of code that runs if an exception occurs. You can catch specific
exceptions by specifying them after except.
• else: The block of code that runs if no exceptions occur in the try block.
• finally: The block of code that runs no matter what, often used for cleanup
actions.
Code example: -
try:
num = int(input("Enter a number: "))
result = 10 / num
print(f"The result is {result}")
except ZeroDivisionError as e:
print("Error: Division by zero is not allowed.")
except ValueError as e:
print("Error: Invalid input. Please enter a numeric value.")
else:
print("The operation was successful.")
➢ Multiple except blocks
We can use multiple except blocks to handle different types of exceptions in a try block. This
allows us to catch and handle specific exceptions differently.
Syntax for Multiple Except Blocks
try:
# Code that may raise an exception
risky_code()
except ExceptionType1:
# Code to handle ExceptionType1
handle_exception1()
except ExceptionType2:
# Code to handle ExceptionType2
handle_exception2()
except (ExceptionType3, ExceptionType4):
# Code to handle ExceptionType3 or ExceptionType4
handle_exception3_or_4()
except Exception as e:
# Code to handle any other exception
handle_generic_exception(e)
else:
# Code to run if no exceptions occur
successful_execution()
finally:
# Code to run regardless of whether an exception occurred or not
cleanup()

Example of Multiple Except Blocks

Here's an example to demonstrate how you can use multiple except blocks:

def divide(a, b):

try:

result = a / b

except ZeroDivisionError:

print("Error: Division by zero is not allowed.")

except TypeError:

print("Error: Both arguments must be numbers.")

except Exception as e:

print(f"Unexpected error: {e}")

else:

print(f"The result is {result}")

finally:

print("Execution completed.")

# Test cases

divide(10, 2) # Valid division

divide(10, 0) # Division by zero

divide(10, 'a') # Invalid type

divide(10, None) # Unexpected error

➢ Multiple exceptions in a single block


you can handle multiple exceptions within a single except block by listing each
exception type you want to catch, separated by commas. This approach allows you to
provide common handling logic for multiple specific exceptions. Here’s how you can use
multiple exceptions in a single except block:

try:

# Code that may raise exceptions

num1 = int(input("Enter a numerator: "))

num2 = int(input("Enter a denominator: "))

result = num1 / num2 # This could raise ZeroDivisionError or ValueError

except ZeroDivisionError:

# Handling division by zero error

print("Error: Division by zero!")

except ValueError:

# Handling invalid input error (non-integer input)

print("Error: Please enter valid integers for numerator and denominator.")

except Exception as e:

# Handling any other unspecified exceptions

print(f"Error: {e}")

else:

# Executes if no exception occurred in the try block

print(f"Result: {result}")

finally:

# Clean-up or finalization code

print("Execution complete.")

➢ Raising exceptions
Raising exceptions in Python allows you to signal that an error or exceptional
condition has occurred during the execution of your code. This is particularly
useful when you encounter situations where normal program flow cannot proceed
due to unexpected conditions.

Example 1

# Define a custom exception class

class CustomError(Exception):

def __init__(self, message):

self.message = message

super().__init__(self.message)

# Function that raises a custom exception

def validate_age(age):

if age < 0:

raise CustomError("Age cannot be negative")

elif age < 18:

raise ValueError("Must be at least 18 years old")

else:

print("Age is valid")

# Example usage of the function

try:

validate_age(-5) # This will raise CustomError

except CustomError as e:

print(f"Custom Error occurred: {e.message}")

except ValueError as e:

print(f"Value Error occurred: {e}")


Example: -2

try:

num = int(input("Enter a positive number: "))

if num <= 0:

raise ValueError("Number must be positive")

except ValueError as e:

print(f"Error: {e}") 0

Note: - raise Statement: In each conditional branch, raise is used to explicitly


raise an exception (CustomError or ValueError).

➢ The finally block


The finally block is used in conjunction with try and except blocks to define a piece
of code that will be executed whether an exception occurs or not. The finally block
ensures that certain cleanup actions are performed, such as closing a file, releasing
resources, or finalizing operations, regardless of whether an exception was raised or not.

Syntax: -

try:

# Code that may raise an exception

# This could be I/O operations, database operations, etc.

print("Try block: Executing code that may raise an exception")

result = 10 / 0 # This will raise a ZeroDivisionError

except ZeroDivisionError:

# Handling specific exceptions

print("Except block: Handling ZeroDivisionError")

finally:

# Code that will always execute, regardless of whether an exception was raised or not
print("Finally block: Cleaning up resources or finalizing operations")

Note: - The ‘finally’ block is optional but if present, must follow all except blocks.

Refer link: - https://ncert.nic.in/textbook/pdf/lecs101.pdf

Assignment questions

1. class Person:

def __init__(self, name, age):

self.name = name

self.age = age

def __lt__(self, other):

return self.age < other.age

p1 = Person("Alice", 20)

p2 = Person("Bob", 30)

print(p1 < p2)

print(p2 < p1)

What is the output of the following

2. class Fruit:

def __init__(self, num):

self.num = num

obj1 = Fruit(10)

obj2 = Fruit(25)

sum = obj1 + obj2

print(sum)
3. What happens if you raise an exception with arguments but do not handle it?
4. How can you catch multiple exceptions at once in Python?
5. What are logic errors?Give Examples.
6. What is the difference between the Exception class and the Base Exception class in
Python?
7. Differentiate error and exception
8. How can you ensure that a certain code block runs no matter whether there’s an
exception or not.
9. What is the output of the following snippet

a = 'welcome to Python'

b = ['welcome', 'Python']

print(a.__len__())

print(len(a))

print( b[0])

print(b.__getitem__(0))

10. Define the term operator Overloading.


11. Define the term operator Overloading. Explain with an example.
12. Give the advantages of operator overloading
13. Differentiate between __add__ and __radd__ Functions
14. Which operator is used to check whether a value is present in the object or not? Write a
python program to overload the operator
15. Write a Program to overload the + operator on complex Object
16. Write a python program to overload relational operator
17. Does Python Support Operator overloading? Justify.
18. Explain the purpose of ‘else’ and ‘finally’ blocks in exception handling with a Python
program.
19. List and explain any 5 Built-in Exceptions available in Python.
20. Explain the usage of ‘except’ clause in Python with multiple exceptions and no
exceptions.
21. Differentiate between error and exception. With example
22. Explain how we can use the assert statement to generate Assertion Error with the help of
an example program.
23. Explain the significance of try-except block with an example.
24. Write a Program to handle to divide by 0 exception.
25. Write a number game program. Ask the user to enter a number. If the number is greater
than the number to be guessed raise a value too large exception. if number is small raise
value too small exception and prompt user to enter again. Quit the program only when the
user enters the correct number.
26. Write a program that finds square root of a number Throw an exception if a negative
number is entered.

You might also like