Exception handling is a process of responding to exceptions that occur during the execution of a program. Without exception handling, programs would not be able to recover from errors.
In normal situations, whenever an exception is encountered, the program comes to an immediate stop. Exception handling allows us to catch the exception, perform a corrective action if necessary, and then continue with the program's execution from the point the exception was raised. This prevents the program from abruptly halting.
Python offers three keywords for use in handling exceptions, these are, try, except and finally. The three keywords provides an organized, structured way to handle the exceptions and continue running the program.
Try and except blocks
The try
and except
blocks are used together to "catch" exceptions as they are raised.
What simply goes on is that, we put the statement(s) that can raise an exception in the try
block and then we put the corrective statements under the except
block. If the statement(s) under the try
block raises an exception, the exception object is passed to the except
block as the statements under its block gets executed.
try:
print(a, b, c)
except:
print("There was an error.")
In the above example, we used undefined variables, a, b, and c. Under normal circumstances, a NameError
would have been raised but by using the try except block, we effectively catch the error and respond accordingly.
Used as above, the except
block acts as a catchall block. It responds to any type of an exception that will be raised except the SyntaxError which can't be handled except when executing code dynamically using the eval(). or the exec() functions.
try:
#this should have raised an IndexError
L = [1, 2, 3, 4]
print(L[10])
except:
print("There was an error.")
try:
#this should have raised an ZeroDivisionError
print(1 / 0)
except:
print("There was an error.")
Did we really "catch" the exception?
In our previous example, we used the try-except blocks without really caring what exception was being raised. However the except block is capable of getting the specific exception that was raised.
Remember that, ultimately, all exceptions are subclasses of the Exception
class. When we catch any exception, an Exception
object is passed to the except
block. We can then alias the exception raised using the "as" clause as shown below:
try:
print(1 / 0)
except Exception as e:
print(e)
Handle the specific exception
In all the previous examples, we handled all exceptions at once. However this is a bad programming practice and should generally be avoided as it can lead to hard-to-debug errors.
The Pythonic way is to be as explicit as possible by handling only the exception that you expect to occur. We achieve this by simply using the except keyword followed by the exception that we want to catch.
try:
print(1 / 0)
except ZeroDivisionError:
print("You divided a number by 0.")
In the above case we only handle the ZeroDivisionError
, any other exception that will occur in the try block will be raised as usual.
#Handle an IndexError
try:
L = [1, 2, 3, 4]
print(L[10])
except IndexError:
print("Invalid index")
#handle a ValueError
#import the math module
import math
try:
print(math.sqrt(-10))
except ValueError:
print("Square root of -ve numbers is undefined")
Multiple except blocks
You can have as many except blocks as necessary. This allows you to handle different kinds of errors that may occur in the same code. However, the try
block will be exited when the first exception is encountered
try:
# statement(s)
except IndexError:
# statement(s)
except ValueError:
# statement(s)
import math
try:
#This will raise a TypeError
print( 3 + 'hello' )
#This will raise a ValueError
math.sqrt(-16)
#This will raise a NameError
print(a)
#This will raise a zeroDivisionError
print(10 / 0)
except NameError:
print("A NameError occurred")
except ValueError:
print("A ValueError occurred")
except ZeroDivisionError:
print("A ZeroDivisionError occurred")
except TypeError:
print("A TypeError occurred")
The else block
One feature about Python is that it allows the use of the else
block in some unexpected places this includes with for
and while
loops and now with the try
blocks.
The else
block when used with the try-except
blocks only gets executed if the try
block terminates successfully i.e no exception was raised inside the
block.
You can only have one else
block in a try-except arrangement and it should be placed after the try
and except
blocks
import math
try:
print("Hello, World!")
print(10/ 2)
print(math.sqrt(9))
except ValueError:
print("a valu error has occurred")
except IndexError:
print("IndexError has occurred")
else:
print("No Exception was raised.")
The 'finally' block
The finally
block executed gets regardless of whether the try block terminated successfully or an exception was raised.
The block should be placed as the last block after the try
, except
and else
blocks.
try:
# Some statements....
except:
# Handling of exception (if required)
else:
# execute if no exception is raised in the try block
finally:
# Some code .....(always executed)
import math
try:
print("Hello, World!")
print(10/ 5)
print(math.sqrt(-9))
except ValueError:
print("a ValueError has occurred")
except IndexError:
print("IndexError has occurred")
else:
print("No Exception was raised.")
finally:
print("This block always gets executed")
Note: Either the except
or the finally
block must be present for the try
block to work. A SyntaxError
will be raised otherwise.