Exceptional Handling

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 24

• Introduction To Exception Handling

• Exception Handling Keywords


• Exception Handling Syntax
• Handling Multiple Exceptions
• Handling All Exceptions
• Using Exception Object
• Getting Details Of Exception
• Raising An Exception
• Using finally Block
• Creating User Defined Exceptions

14.1 Errors and Exception Handling


In this section, we will learn about Errors and Exception Handling in Python. You've might
have definitely encountered errors by this point in the course. For example:
print('Hello')

Hello

print('Hello)

File
"C:\Users\sunny\AppData\Local\Temp/ipykernel_35952/1679058590.py",
line 1
print('Hello)
^
SyntaxError: EOL while scanning string literal

Note how we get a SyntaxError, with the further description that it was an End of Line
Error (EOL) while scanning the string literal. This is specific enough for us to see that we
forgot a single quote at the end of the line. Understanding of these various error types will
help you debug your code much faster.
This type of error and description is known as an Exception. Even if a statement or
expression is syntactically correct, it may cause an error when an attempt is made to
execute it. Errors detected during execution are called exceptions and are not
unconditionally fatal.

Exception description
Exception Base class of exception. Allother exception
derived from this class.
ArithmeticError Base class for those exceptions that are
raised for arithmetic or numeric errors.
ZeroDivisionError Raised when division or modulo operation
is zero
ModuleNotFoundError Raised by import when imported module
could not be located
KeyError Raised when a mapping (dictionary) key is
not found in keys of a dictionary.
MemoryError Raised when an operation runs out of
memory

You can check out the full list of built-in exceptions here. Now, let's learn how to handle
errors and exceptions in our own code.
In python, There are two popular saying for code styles:-
1) LBYL
2) EAFP

LBYL
Look before you leap. This coding style explicitly tests for pre-conditions before making
calls or lookups. This style contrasts with the EAFP approach and is characterized by the
presence of many if statements.
In a multi-threaded environment, the LBYL approach can risk introducing a race condition
between “the looking” and “the leaping”. For example, the code, if key in mapping: return
mapping[key] can fail if another thread removes key from mapping after the test, but
before the lookup. This issue can be solved with locks or by using the EAFP approach.
For this, you can refer in python documentation:-
** https://docs.python.org/3/glossary.html **
In a simple language, we first check what we are going to do. For example, if we want to
check if a file is available before trying to write:
if filename:
with open()..
....

EAFP
Easier to ask for forgiveness than permission.
This common Python coding style assumes the existence of valid keys or attributes and
catches exceptions if the assumption proves false. This clean and fast style is characterized
by the presence of many try and except statements. The technique contrasts with the LBYL
style common to many other languages such as C.
In a simple language,EAFP is like we first write our code so that it performs and executes
first, and then we will take care of the consequences if it doesn't work. That means we try
running some code, expecting it to work, but if it failes then will handle it in exception in
except block.
try:
with open(filename,'r').. # First executes
....
except:
# handling exception

14.2 try and except


The basic terminology and syntax used to handle errors in Python is the try and except
statements. The code which can cause an exception to occur is put in the try block and the
handling of the exception are the implemented in the except block of code. The syntax form
is:
try:
You do your operations here...
...
except ExceptionI:
If there is ExceptionI, then execute this block.
except ExceptionII:
If there is ExceptionII, then execute this block.
...
else:
If there is no exception then execute this block.

Using just except, we can check for any exception: To understand better let's check out a
sample code that opens and writes a file:
try:
f = open('demo_file','w')
f.write('Test write this')
except IOError:
# This will only check for an IOError exception and then execute
this print statement
print("Error: Could not find file or read data")
else: # this else block will execute if try block does not raise an
exception
print("Content written successfully")
f.close()

Content written successfully

Now, let's see what happens when we don't have write permission? (opening only with 'r'):
f = open('demo_file','r')
f.write('Test write this')
print("fsfsfsfsf")
----------------------------------------------------------------------
-----
UnsupportedOperation Traceback (most recent call
last)
<ipython-input-2-7498bff3140a> in <module>
1 f = open('testfile','r')
----> 2 f.write('Test write this')
3 print("fsfsfsfsf")

UnsupportedOperation: not writable

try:
f = open('demo_file','r')
f.write('Test write this')
except IOError:
# This will only check for an IOError exception and then execute
this print statement
print("Error: Could not find file or read data")
else:
print("Content written successfully")
f.close()

Error: Could not find file or read data

Notice, how we only printed a statement! The code still ran and we were able to continue
doing actions and running code blocks. This is extremely useful when you have to account
for possible input errors in your code. You can be prepared for the error and keep running
code, instead of your code just breaking as we saw above.
We could have also just said except: if we weren't sure what exception would occur. For
example:
try:
f = open('testfile','r')
f.write('Test write this')
except:
# This will check for any exception and then execute this print
statement
print("Error: Could not find file or read data")
else:
print("Content written successfully")
f.close()

Error: Could not find file or read data

Now, we don't actually need to memorize the list of exception types! Now what if we keep
wanting to run code after the exception occurred? This is where finally comes in.
finally
The finally: Block of code will always be run regardless if there was an exception in the try
code block. The syntax is:
try:
Code block here
...
Due to any exception, this code may be skipped!
finally:
This code block would always be executed.

For example:
try:
f = open("testfile", "w")
f.write("Test write statement")
finally:
print("Always execute finally code blocks")

Always execute finally code blocks

We can use this in conjunction with except. Let's see a new example that will take into
account a user putting in the wrong input:
def askint():
try:
val = int(input("Please enter an integer: "))
except:
print("Looks like you did not enter an integer!")

finally:
print("Finally, I executed!")
print(val)

askint()

Please enter an integer: 66


Finally, I executed!
66

askint()

Please enter an integer: six


Looks like you did not enter an integer!
Finally, I executed!

----------------------------------------------------------------------
-----
UnboundLocalError Traceback (most recent call
last)
<ipython-input-8-cc291aa76c10> in <module>
----> 1 askint()

<ipython-input-6-1b367de9a831> in askint()
7 finally:
8 print("Finally, I executed!")
----> 9 print(val)

UnboundLocalError: local variable 'val' referenced before assignment

Check how we got an error when trying to print val (because it was properly assigned).
Let's find the right solution by asking the user and checking to make sure the input type is
an integer:
def askint():
try:
val = int(input("Please enter an integer: "))
except:
print("Looks like you did not enter an integer!")
try:
val = int(input("Try again-Please enter an integer:
"))
except:
print("handle it ")
finally:
print("Finally, I executed!")

askint()

Please enter an integer: six


Looks like you did not enter an integer!
Try again-Please enter an integer: 44
Finally, I executed!

Hmmm...that only did one check. How can we continually keep checking? We can use a
while loop!
def askint():
while True:
try:
val = int(input("Please enter an integer: "))
except:
print("Looks like you did not enter an integer!")
break
else:
print('Yep thats an integer!')
break
finally:
print("Finally, I executed!")
print(val)

askint()
Please enter an integer: five
Looks like you did not enter an integer!
Finally, I executed!

----------------------------------------------------------------------
-----
UnboundLocalError Traceback (most recent call
last)
<ipython-input-15-cc291aa76c10> in <module>
----> 1 askint()

<ipython-input-14-9682f0bcb1dc> in askint()
11 finally:
12 print("Finally, I executed!")
---> 13 print(val)

UnboundLocalError: local variable 'val' referenced before assignment

Raising an exception
We can raise an excepton by using raise statement. Let us check with examples:-
def raise_exc(a):
if a < 5:
raise Exception(a) # If exception raised code below to this
will not execute

return a

try:
res1 = raise_exc(7)
print(res1)
res = raise_exc(2)
print(res)
except Exception as e:
print("Error is ", e)

7
Error is 2

User defined exception


Developers can create their own exceptions by creating a new exception which will be
derived from the Exception class.The newly created exception nae should be ended with
“Error” similar to naming of the standard exceptions in python.
''' Create user-defined exception
MyError is derived from super class Exception
'''
class NewError(Exception):
# Constructor or Initializer
def __init__(self, val):
self.val = val

# __str__ is to print() the value


def __str__(self):
return(repr(self.val))

try:
raise(NewError(5))
except NewError as e:
print('A New Exception occured: ',e.val)

A New Exception occured: 5

Assertion
Python's assert statement helps you find bugs more quickly and with less pain.
Assertions are a systematic way to check that the internal state of a program is as the
programmer expected, with the goal of catching bugs. In particular, they're good for
catching false assumptions that were made while writing the code, or abuse of an interface
by another programmer. In addition, they can act as in-line documentation to some extent,
by making the programmer's assumptions obvious. ("Explicit is better than implicit.")
For more,
Refer:- https://wiki.python.org/moin/UsingAssertionsEffectively
from IPython.display import Image
Image(filename='img/assertion.png')

# Assert for comparing two variables


try:
x = 1
y = 1
assert x != y, "Both variables are not the same"
print(x , y)

# the error message we provided gets printed


except AssertionError as msg:
print(msg)

Both variables are not the same

# Looping through list and print division of given element with the
element itself if element is int else raise exception

list1 = ['a', 0, 2] # For str 'a' raise error, for 0 raise zero
division error

for el in list1:
try:
print("The element is", el)
res = int(el)/int(el)
break
except Exception as e:
print("Oo!", e, "occurred.")
print("Next check for another element in a list.")
print()
print(res)

The element is a
Oo! invalid literal for int() with base 10: 'a' occurred.
Next check for another element in a list.

The element is 0
Oo! division by zero occurred.
Next check for another element in a list.

The element is 2
1.0

# try with if condition if not positive number it will raise an


exception using raise
try:
a = int(input("Enter a positive integer: "))
if a <= 0:
raise ValueError("That is not a positive number!")
except ValueError as ve:
print(ve)

Enter a positive integer: -8


That is not a positive number!
# Simple program for even number using try else and assert
try:
num = int(input("Enter a number: "))
assert num % 2 == 0
except:
print("Not an even number!")
else:
print("Executing else block as try block hasn't raise an
exception.")

Enter a number: 32
Executing else block as try block hasn't raise an exception.

# Defining function for raising exception


def raise_exception(ex):
raise ex

# Use of lambad function with exception raise if value < 10 will raise
an exception
y = lambda x: 2*x if x < 10 else raise_exception(Exception('Value is
greater than 10'))

y(5)

10

y(20)

----------------------------------------------------------------------
-----
Exception Traceback (most recent call
last)
<ipython-input-57-69bf4e317ad3> in <module>
----> 1 y(20)

<ipython-input-55-99236a7fd7fe> in <lambda>(x)
1 # Use of lambad function with exception raise if value < 10
will raise an exception
----> 2 y = lambda x: 2*x if x < 10 else
raise_exception(Exception('Value is greater than 10'))

<ipython-input-54-a8f242b090b9> in raise_exception(ex)
1 # Defining function for raising exception
2 def raise_exception(ex):
----> 3 raise ex

Exception: Value is greater than 10

# defining function for finding sqrt and checking type of number and
raise exception if not an int/float
import math
def find_sqrt(a):
if not isinstance(a,(int,float)):
raise TypeError("a must be numeric")
elif a < 0:
raise TypeError("a should not be negative")
else:
print(math.sqrt(a))

find_sqrt("scsc")

----------------------------------------------------------------------
-----
TypeError Traceback (most recent call
last)
<ipython-input-2-31a3948965a9> in <module>
----> 1 find_sqrt("scsc")

<ipython-input-1-e201b0a49914> in find_sqrt(a)
3 def find_sqrt(a):
4 if not isinstance(a,(int,float)):
----> 5 raise TypeError("a must be numeric")
6 elif a < 0:
7 raise TypeError("a should not be negative")

TypeError: a must be numeric

find_sqrt(-56)

----------------------------------------------------------------------
-----
TypeError Traceback (most recent call
last)
<ipython-input-3-24946c882679> in <module>
----> 1 find_sqrt(-56)

<ipython-input-1-e201b0a49914> in find_sqrt(a)
5 raise TypeError("a must be numeric")
6 elif a < 0:
----> 7 raise TypeError("a should not be negative")
8 else:
9 print(math.sqrt(a))

TypeError: a should not be negative

find_sqrt(6)

2.449489742783178

What Is An Exception ?
Exception are errors that occur at runtime .
In other words , if our program encounters an abnormal situation during it’s execution it
raises an exception.
For example,the statement
a=10/0

will generate an exception because Python has no way to solve division by 0

What Python Does When An Exception Occurs ?


Whenever an exception occurs , Python does 2 things :
It immediately terminates the code
It displays the error message related to the exception in a technical way
Both the steps taken by Python cannot be considered user friendly because Even if a
statement generates exception , still other parts of the program must get a chance to run
The error message must be simpler for the user to understand

A Sample Code
a=int(input("Enter first no:"))
b=int(input("Enter second no:"))
c=a/b
print("Div is",c)
d=a+b
print("Sum is",d)

Enter first no:10


Enter second no:a

----------------------------------------------------------------------
-----
ValueError Traceback (most recent call
last)
<ipython-input-10-8366b991657c> in <module>
1 a=int(input("Enter first no:"))
----> 2 b=int(input("Enter second no:"))
3 c=a/b
4 print("Div is",c)
5 d=a+b

ValueError: invalid literal for int() with base 10: 'a'

As we can observe , in the second run the code generated exception because Python does
not know how to handle division by 0. Moreover it did not even calculated the sum of 10
and 0 which is possible
a=int(input("Enter first no:"))
b=int(input("Enter second no:"))
c=a/b
print("Div is",c)
d=a+b
print("Sum is",d)

In this case since it is not possible for Python to covert “2a” into an integer , so it generated
an exception . But the message it displays is too technical to understand

How To Handle Such Situations ?


If we want our program to behave normally , even if an exception occurs , then we will have
to apply Exception Handling
Exception handling is a mechanism which allows us to handle errors gracefully while the
program is running instead of abruptly ending the program execution.
Python provides 5 keywords to perform Exception Handling:
try
except
else
raise
finally
Following is the syntax of a Python try-except-else block.
try:
You do your operations here;
......................

except ExceptionI:
If there is ExceptionI, then execute this block.

except ExceptionII:
If there is ExceptionII, then execute this block.
......................

else:
If there is no exception then execute this block.

Remember ! In place of Exception I and Exception II , we have to use the names of


Exception classes in Python
a=int(input("Enter first no:"))
b=int(input("Enter second no:"))
try:
c=a/b
print("Div is",c)
except ZeroDivisionError as e:
print(e)
d=a+b
print("Sum is",d)

Enter first no:10


Enter second no:0
division by zero
Sum is 10

#Amongst all the exceptions mentioned in the previous slides, we


cannot handle SyntaxError exception , because it is raised by Python
even before the program starts execution

#Example:

a=int(input("Enter first no:"))


b=int(input("Enter second no:"))
try:
c=a/b
print("Div is",c))
except SyntaxError:
print("Wrong Syntax")
d=a+b
print("Sum is",d)

File "<ipython-input-13-2fd3a05b1c9d>", line 9


print("Div is",c))
^
SyntaxError: unmatched ')'

A try statement may have more than one except clause for different exceptions.But at most
one except clause will be executed
Also , we must remember that if we are handling parent and child exception classes in
except clause then the parent exception must appear after child exception , otherwise child
except will never get a chance to run
import math
try:

x=10/5
print(x)
ans=math.exp(3)
print(ans)

except ZeroDivisionError:
print("Division by 0 exception occurred!")
except ArithmeticError:
print("Numeric calculation failed!")

2.0
20.085536923187668

import math
try:

x=10/0
print(x)
ans=math.exp(20000)
print(ans)

except ZeroDivisionError:
print("Division by 0 exception occurred!")
except ArithmeticError:
print("Numeric calculation failed!")

Division by 0 exception occurred!

import math
math.exp(20000)

----------------------------------------------------------------------
-----
OverflowError Traceback (most recent call
last)
<ipython-input-16-34b2787f9eac> in <module>
1 import math
----> 2 math.exp(20000)

OverflowError: math range error

import math
try:

x=10/5
print(x)
ans=math.exp(20000)
print(ans)

except ZeroDivisionError:
print("Division by 0 exception occurred!")
except ArithmeticError:
print("Numeric calculation failed!")

2.0
Numeric calculation failed!

import math
try:

x=10/5
print(x)
ans=math.exp(20000)
print(ans)

except ArithmeticError:
print("Numeric calculation failed!")
except ZeroDivisionError:
print("Division by 0 exception occurred!")

2.0
Numeric calculation failed!

import math
try:

x=10/0
print(x)
ans=math.exp(20000)
print(ans)

except ArithmeticError:
print("Numeric calculation failed!")
except ZeroDivisionError:
print("Division by 0 exception occurred!")

Numeric calculation failed!


exercise
Write a program to ask the user to input 2 integers and calculate and print their division.
Make sure your program behaves as follows:
If the user enters a non integer value then ask him to enter only integers If denominator is
0 , then ask him to input non-zero denominator Repeat the process until correct input is
given
Only if the inputs are correct then display their division and terminate the code
while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except ValueError:
print("Please input integers only! Try again")
except ZeroDivisionError:
print("Please input non-zero denominator")

If we want to write a single except clause to handle multiple exceptions , we can do this .


For this we have to write names of all the exceptions within parenthesis separated with
comma after the keyword except
while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except (ValueError,ZeroDivisionError):
print("Either input is incorrect or denominator is 0. Try
again!")

Input first no:0


Input second no:0
Either input is incorrect or denominator is 0. Try again!
Input first no:2
Input second no:a
Either input is incorrect or denominator is 0. Try again!
Input first no:5
Input second no:2
Div is 2.5

We can write the keyword except without any exception class name
also .
In this case for every exception this except clause will run .

The only problem will be that we will never know the type of exception
that has occurred!

Following is the syntax of a Python handle all exception  block.

try:
You do your operations here;
......................
except :
For every kind of exception this block will execute

Notice , we have not provided any name for the exception


while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except:
print("Some problem occurred. Try again!")

Input first no:10


Input second no:0
Some problem occurred. Try again!
Input first no:12
Input second no:9
Div is 1.3333333333333333

Using Exception Object

Now we know how to handle exception, in this section we will learn how to access
exception object in exception handler code.
To access the exception object created by Python we can use the keyword as and assign it
to a variable.
Finally using that variable we can get the details of the exception
a=10
b='c'
d=a/b
print(d)
----------------------------------------------------------------------
-----
TypeError Traceback (most recent call
last)
<ipython-input-2-310ad5b1b85b> in <module>
1 a=10
2 b='c'
----> 3 d=a/b
4 print(d)

TypeError: unsupported operand type(s) for /: 'int' and 'str'

a=int(input("Input first no:"))


b=int(input("Input second no:"))
c=a/b
print(c)

Input first no:10


Input second no:a

----------------------------------------------------------------------
-----
ValueError Traceback (most recent call
last)
<ipython-input-3-ea06110b60f0> in <module>
1 a=int(input("Input first no:"))
----> 2 b=int(input("Input second no:"))
3 c=a/b
4 print(c)

ValueError: invalid literal for int() with base 10: 'a'

while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except (ValueError,ZeroDivisionError) as e:
print(e)

Input first no:10


Input second no:0\
invalid literal for int() with base 10: '0\\'

Python allows us to get more details about the exception by calling the function exc_info().
Following are it’s important points:
It is available in the module sys
It returns a tuple containing 3 items called type, value and traceback
The variable type is the name of exception class, value is the instance of exception class and
traceback contains the complete trace of the exception
import sys
while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except:
a,b,c=sys.exc_info()
print("Exception class:",a)
print("Exception message:",b)
print("Line number:",c.tb_lineno)

Input first no:10


Input second no:0
Exception class: <class 'ZeroDivisionError'>
Exception message: division by zero
Line number: 6
Input first no:10
Input second no:5
Div is 2.0

Sometimes , we need to print the details of the exception exactly like Python does .
We do this normally , when we are debugging our code.
The class traceback which is available in the module traceback helps us do this

This class contains a method called format_exc( )

It is a class method of traceback class and returns complete details


of the exception as a string.

This string contains:


The program name in which exception occurred
Line number where exception occurred
The code which generated the exception
The name of the exception class
The message related to the exception

import traceback
while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except:
print(traceback.format_exc())

Input first no:10


Input second no:0
Traceback (most recent call last):
File "<ipython-input-7-18a6bcc5f981>", line 5, in <module>
c=a/b
ZeroDivisionError: division by zero

Input first no:10


Input second no:6
Div is 1.6666666666666667

Raising An Exception
We can force Python to generate an Exception using the keyword raise.
This is normally done in those situations where we want Python to throw an exception in a
particular condition of our choice
Syntax: raise ExceptionClassName
raise ExceptionClassName( message )
Write a program to ask the user to input 2 integers and calculate and print their division.
Make sure your program behaves as follows:
If the user enters a non integer value then ask him to enter only integers If denominator is
0 , then ask him to input non-zero denominator If any of the numbers is negative then
display the message negative numbers not allowed Repeat the process until correct input is
given
Only if the inputs are correct then display their division and terminate the code
while(True):
a=int(input("please write a input:"))
if a>10:
break
print("please enter greater than 10")

please write a input:9


please enter greater than 10
please write a input:8
please enter greater than 10
please write a input:7
please enter greater than 10
please write a input:10
please enter greater than 10
please write a input:10
please enter greater than 10
please write a input:11

while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
if a<0 or b<0:
raise Exception("Negative numbers not allowed!Try
again")
c=a/b
print("Div is ",c)
break;
except ValueError:
print("Please input integers only! Try again")
except ZeroDivisionError:
print("Please input non-zero denominator")
except Exception as e:
print(e)

Input first no:10


Input second no:a
Please input integers only! Try again
Input first no:10
Input second no:0
Please input non-zero denominator
Input first no:10
Input second no:-4
Negative numbers not allowed!Try again
Input first no:10
Input second no:5
Div is 2.0

If we have a code which we want to run in all situations, then we should write it inside the
finally block.
Python will always run the instructions coded in the finally block.
It is the most common way of doing clean up tasks , like, closing a file or disconnecting with
the DB or logging out the user etc
The finally block has 2 syntaxes:
Syntax 1
try:
# some exception generating code
except :
# exception handling code
finally:
# code to be always executed
try:
# some exception generating code
finally:
# code to be always executed

while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
c=a/b
print("Div is ",c)
break;
except ZeroDivisionError:
print("Denominator should not be zero")
finally:
print("Thank you for using the app!")

Python has many built-in exceptions which forces our program to output an error when
something in it goes wrong.
However, sometimes we may need to create our own exceptions which will be more
suitable for our purpose.
Such exceptions are called User Defined Exceptions
In Python, users can define such exceptions by creating a new class.
This exception class has to be derived, either directly or indirectly, from Exception class.
Most of the built-in exceptions are also derived form this class.
class NegativeNumberException(Exception):
def __init__(self):
print("please type a pos number")

while(True):
try:
a=int(input("Input first no:"))
b=int(input("Input second no:"))
if a<0 or b<0:
raise NegativeNumberException()
c=a/b
print("Div is ",c)
break;
except ValueError:
print("Please input integers only! Try again")
except ZeroDivisionError:
print("Please input non-zero denominator")
except NegativeNumberException as e:
print(e)

Input first no:10


Input second no:-1
please type a pos number

import math
ans=math.exp(20000)

----------------------------------------------------------------------
-----
OverflowError Traceback (most recent call
last)
<ipython-input-8-660e2d42fc5d> in <module>
1 import math
----> 2 ans=math.exp(20000)

OverflowError: math range error

ans

7.38905609893065

You might also like