How to handle exceptions in python
Prerequisite: Exceptions and errors in Python
What are Exceptions?
Exceptions are run-time errors that unexpectedly occur and crash a program. When occurred, it alters the flow of the program. These errors are not recognized in syntax checks, and are recognized only when the line with the error is executed. Programmers can handle these exceptions in Python using a special process, and in this article, we will discuss this whole concept of exception handling.
Example:
#Exception occurs when we try to open a non-existing file
print ("opening a non existing file")
file1 = open ("textfile.txt")
print (file1.read ())
file1. close ()
Output:
opening a non-existing file
Traceback (most recent call last):
File "D:\Programs\python\nofile.py", line 2, in <module>
file1 = open ("textfile.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'textfile.txt'
Exception handling
We can handle the exceptions using try and except statements. These are pre-defined statements in the Python libraries. The block of code that might raise an exception that the programmer doubts are written in the try block and are handled in the except block.
Syntax:
try:
# Statements that might raise an exception
except name_of_the_exception: #name is optional
#Statements to handle the exception if raised
With try and except statements, we try to execute the code, and also now we're ready for any exception that can occur. We write the code to handle the exception in the except block.
Generally, when an exception occurs, the program gets disturbed and halts when the line with the exception is executed to display the error message. Here, we won't let the program flow stop, and the flow will normally go after the exception is handled.
Flow of program with try and except
First, the try block is executed. If the interpreter finds an exception in the try block, it finds the except block and executes it. If there is no exception in the code inside the try block, except block will not be executed.
Example program1:
Let us take the same example as above.
print ("opening a non existing file")
try:
file1 = open ("textfile.txt") #The file does not exist
print (file1.read ())
file1.close ()
except:
print ("The file does not exist. Create a new one")
Output:
opening a non-existing file
The file does not exist. Create a new one
Explanation:
As you can observe, we did not run into any exceptions. We kept the part of code of opening a non-existing file in the try block. If the file would exist, it simply opens the file and does not consider the except block. However, in the above code, the file is not present, it finds the except block and executes it.
Here is another example program:
Example Program 2:
#ZeroDivisionError
print ("Division by zero")
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
quotient = a/b
print ("The quotient is: ",quotient)
except:
print ("Division by zero is not possible, check the divisor")
Output:
#with exception rose
Division by zero
Enter the dividend: 4
Enter the divisor: 0
Division by zero is not possible; check the divisor.
#with no exception
Division by zero
Enter the dividend: 4
Enter the divisor: 2
The quotient is: 2.0
Explanation:
In the first case, the divisor is 0, which raised an exception. Hence, the except block is executed. In the second case, the divisor is 2, and there is no other exception. Hence, the except block is not executed, and the next statement after the division in the try block is executed, and the quotient is printed.
- When an exception is raised, and the except block is executed, we say we caught the exception and handled it.
- We cannot use a try block without except. Instead, we can use the pass statement in the except block to leave it empty.
There are a lot of possible exceptions the programmer can run into. To handle these exceptions, we can either use the exception's exact name or the base class for all the exceptions - Exception.
Note: Check the exception hierarchy here.
Catching a Particular Exception
If we already know what kind of exception we might run into, we can mention the name of the exception right after the except statement. However, if any other exception is raised other than that exception before it occurs, it won't be handled, and the exception will be raised.
Example program:
print ("Division by zero")
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
a = [1,2]
print (a [2])
quotient = a/b
print ("The quotient is: ",quotient)
except ZeroDivisionError:
print ("Zero can't be the divisor")
Output:
Division by zero
Enter the dividend: 4
Enter the divisor: 2
Traceback (most recent call last):
File "D:\Programs\python\arrayindex.py", line 6, in <module>
print (a [2])
IndexError: list index out of range
Explanation:
We mentioned ZeroDivisionError in the except block, but before it even occurred, in the try block, we ran into another exception – trying to access an index that does not exist in the list – IndexError, which we have not handled. Hence, the exception is raised.
Handling multiple exceptions
A try block can have multiple except blocks, which means we can handle different exceptions with different codes. But we cannot handle all the exceptions in the try block simultaneously. At most, one exception is handled at a time because, as soon as we run into an exception, the interpreter will stop the execution of the try block, and it finds the except block with that exception. The try block will not be executed again.
Therefore, to handle multiple exceptions, we need to execute the block with one exception by commenting on the other lines one after the other.
Syntax:
try:
#Statements with multiple exceptions
except IndexError:
#Handling index error
except ValueError:
#Handling value error
except ZeroDivisionError:
#Handling Zero division error
.
.
.
Example:
Exception1:
#IndexError block commented
print ("Division by zero")
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
#a = [1,2]
#print (a [2])
quotient = a/b
print ("The quotient is: ",quotient)
except ZeroDivisionError:
print ("Zero can't be the divisor")
except IndexError:
print ("Index out of range in the list")
Output:
Division by zero
Enter the dividend: 4
Enter the divisor: 2
The quotient is: 2.0
Exception 2:
#ZeroDivisionError block commented
print ("Index out of range")
try:
#a = int (input ("Enter the dividend: "))
#b = int (input ("Enter the divisor: "))
a = [1,2]
print (a [2])
#quotient = a/b
#print ("The quotient is: ",quotient)
except ZeroDivisionError:
print ("Zero can't be the divisor")
except IndexError:
print ("Index out of range in list")
Output:
Index out of range
Index out of range in list
Try block with else:
Generally, when there is no exception in the try block, the except block is not executed, and the whole try block will be executed. After all the except statements, we can use an else block to write a code that will be executed if there is no exception in the try block by just writing the statement that might raise an exception in the try block.
Example program:
print ("zero division")
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
quotient = a/b #line with exception
except ZeroDivisionError:
print ("Zero can't be the divisor")
else:
print ("The quotient is: ", quotient) #if no exception
Output:
#If an exception is raised
zero division
Enter the dividend: 4
Enter the divisor: 0
Zero can't be the divisor
#If an exception is not raised
zero division
Enter the dividend: 4
Enter the divisor: 2
The quotient is: 2.0 -> else block
finally
finally is a keyword in Python. It is also a block like try and except, and as the name suggests, we write it at last. The interpreter will execute it irrespective of whether there is an exception. We write the statements that must be mandatorily executed in the finally block.
If there is an else block, the finally block is written after the else block.
Example program:
print ("zero division")
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
quotient = a/b
except ZeroDivisionError:
print ("Zero can't be the divisor")
else:
print ("The quotient is: ", quotient)
finally:
print ("This is the finally block")
Output:
#When an exception is raised:
zero division
Enter the dividend: 4
Enter the divisor: 0
Zero can't be the divisor
This is the finally block
#When an exception is not raised
zero division
Enter the dividend: 4
Enter the divisor: 2
The quotient is: 2.0
This is the finally block
There might be two questions in your head right now:
1. Why else block when we can write it all down in the try block itself?
There is a difference, but the use is only in a couple of situations:
- The else block stops the try block from catching accidental exceptions to stop finding the exception originally meant to be found. If we do not want to catch that exception, we will keep that block in the other block. And also, it will increase the readability of the code and improve the format.
2. Why finally block when we can write after the blocks normally?
We can use finally block to close the files and resources we opened in the code, and also the main difference is that:
- If there is an exception in the except block, the code we write in the finally block is executed before the exception is raised. But, if we write it normally after the except, it won’t be executed.
- If we run into an unexpected exception that is not handled in the written except blocks, the code in the finally is executed, and if it is not in the finally block, it will not get executed.
Example program:
try:
a = int (input ("Enter the dividend: "))
b = int (input ("Enter the divisor: "))
quotient = a/b
print ("The quotient is: ",quotient)
except ZeroDivisionError:
print ("Zero can't be the divisor")
a = [1,2]
c = int (input ("Enter c: "))
print (a [1]/c)
print ("finally")
Output:
Enter the dividend: 4
Enter the divisor: 0
Zero can't be the divisor
Enter c: 0
Traceback (most recent call last):
File "D:\Programs\python\text_2_speech.py", line 5, in <module>
quotient = a/b
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Programs\python\p1.py", line 11, in <module>
print (a [1]/c)
ZeroDivisionError: division by zero
The last statement has not executed. However, we can use the finally statement to print that last statement:
finally:
print (“finally”)
Output:
Enter the dividend: 4
Enter the divisor: 0
Zero can't be the divisor
Enter c: 0
finally
Traceback (most recent call last):
File "D:\Programs\python\text_2_speech.py", line 5, in <module>
quotient = a/b
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Programs\python\p1.py", line 11, in <module>
print (a [1]/c)
ZeroDivisionError: division by zero
Explanation:
When we were trying to handle the exception in the try, we ran into an exception in the except block, which was not handled. Now, when we use the finally block, the code is executed before the exception is raised, but if we don't use the finally statement, the last statements are not executed.
Note: The lightened output is the raised exception.
User-defined Exceptions
Python also allows the programmer to create exceptions. The use and purpose here are to apply some rules and regulations on variables and create an experience on a project we're working on.
Raising an exception explicitly
We can raise an exception explicitly using the raise statement. We need to use this statement and the exception inside the try block. We can handle this by using the except statement.
Example program:
try:
raise ZeroDivisionError
except ZeroDivisionError:
print ("Exception")
Output:
Exception
- We can use a raise statement again to re-raise the created exception:
Example:
try:
raise ZeroDivisionError
except ZeroDivisionError:
print ("Exception")
raise
Output:
Exception
Traceback (most recent call last):
File "D:\Programs\python\exceptionraise.py", line 2, in <module>
raise ZeroDivisionError
ZeroDivisionError
Examples of different Exceptions and handling them:
1. NameError
It occurs when we try to access a variable or a function that is not present or defined in the program.
2. TypeError
When we use incompatible data type variables or entities to perform operations, like when we try to do arithmetic operations on integer and string data types, the interpreter will raise the type error.
3. KeyError
It occurs when we try to access a key not present in the dictionary's set of existing keys.
4. KeyboardInterrupt
It occurs when the user accidentally clicks the interruption keys on the keyboard, which can be the control-c or delete buttons.
5. OverFlowError
It occurs when the result of an arithmetic operation is to too large to be printed or accessed.
6. ZeroDivisionError
It occurs when we try to divide a number by 0.