Python Exception Handling
Overview
Exceptions are errors that are detected during execution. Whenever there is an error in a program, exceptions are raised.
If these exceptions are not handled, it drives the program into a halt state. Exception handling in python is required to prevent the program from terminating abruptly. This article will further explain Exception Handling in Python.
Introduction
An exception is a class in Python that represents an error. When an exception is raised during the execution of code, it disrupts the normal flow of the program and can lead to a halt state or even a crash. To avoid this, it's important to do Exception handling in Python in a way that allows the program to continue running even if an error occurs.
Exception handling in Python is achieved using the try and except blocks. The try block is used to enclose the code that may raise an exception, while the except block is used to handle the exception that may occur. If an exception is raised in the try block, the program jumps to the except block to handle it, instead of halting the program altogether.
The basic syntax of a try-except block in Python is as follows:
It's important to note that try and except blocks must be used together in order to handle exceptions properly. Writing just try or except blocks alone will result in an error.In addition, the except block can specify the type of exception that it wants to catch.
Common Exceptions in Python
Before proceeding further, we need to understand what some of the common exceptions that python throws are.
All the inbuilt exceptions in python are inherited from the common ‘Exception’ class. Some of the common inbuilt exceptions are:
Exception Name | Description |
---|---|
Exception | All exceptions inherit this class as the base class for all exceptions. |
StopIteration | Raised when the next() method of an iterator while iteration does not point to any object |
StandardError | All exceptions inherit this class except stop StopIteration and SystemExit |
ArithmeticError | Errors that occur during numeric calculation are inherited by it. |
OverflowError | When a calculation exceeds the max limit for a specific numeric data type |
ZeroDivisionError | Raised when division or modulo by zero takes place. |
AssertionError | Raised when an assert statement fails |
AttributeError | Raised in case of failure of attribute assignment |
EOFError | Raised when the end of file is reached, usually occurs when there is no input from input() function |
ImportError | Raised when an import statement fails |
NameError | Raised when an identifier is not found in the local or non-local or global scope. |
SyntaxError | Raised when there is an error in python syntax. |
IndentationError | Raised when indentation is not proper |
TypeError | Raised when a specific operation of a function is triggered for an invalid data type |
ValueError | Raised when invalid values are provided to the arguments of some builtIn function for a data type that has a valid type of arguments. |
RuntimeError | Raised when an error does not fall into any other category |
NotImplementedError | Raised when an abstract method that needs to be implemented in an inherited class is not actually implemented. |
These are just some of the common exceptions in python. To understand more on types of exceptions check python’s official documentation on exceptions
Catching Specific Exceptions in Python
In the above example, we caught the exception that was being raised in the try block, but the except blocks are going to catch all the exceptions that try might raise.
Well, it’s considered a good practice to catch specific types of exceptions and handle them accordingly. And yes, try can have multiple except blocks. We can also use a tuple of values in an except to receive multiple specific exception types.
Let’s take an example to understand this more deeply:
Output:
- In the above code the exceptions raised totally depend upon the input that the user might enter. Hence if a user enters a value as 0 for ‘b’, the python interpreter will raise a ZeroDivisionError.
- And as the array ‘a’ has a length of 3, and the user is trying to access an element at index 3, an IndexError will be raised by the python interpreter.
- Each except block has been defined for both the exceptions as one of them receives exceptions of type IndexError and the other receives of type ZeroDivisionError.
If we want the above snippet to catch exception for both IndexError OR ZeroDivisionError, it can be re-written as:
Output:
Note: Only one of the except blocks is triggered when an exception is raised. Consider an example where we have one except as except IndexError and another as except(IndexError, ZeroDivisionError) then the one written first will trigger.
Raising Custom Exceptions
Even though exceptions in python are automatically raised in runtime when some error occurs. Custom and predefined exceptions can also be thrown manually by raising it for specific conditions or a scenario using the raise keyword. (A custom exception is an exception that a programmer might raise if they have a requirement to break the flow of code in some specific scenario or condition) String messages supporting the reasons for raising an exception can also be provided to these custom exceptions.
Syntax to Raise an Exception
For example:
Output:
- In the above code, the variable ‘a’ can hold whatever value that is assigned to it. Here we assign it a number and we’re passing to a custom method isStringEmpty that checks if a string is an empty string.
- But we orchestrated it to throw a TypeError, by assigning ‘a’ variable a number.
- In the method, we’re checking if the variable is a string or not and if it holds a value or not. In this case, it is supposed to be a string, but assigned it as a number as we’re raising an exception by using
try except and ELSE!
Sometimes you might have a use case where you want to run some specific code only when there are no exceptions. For such scenarios, the else keyword can be used with the try block. Note that this else keyword and its block are optional.
Syntax With Else Clause
Example:
When an exception is not raised, it flows into the optional else block.
Output:
In the above code, As both the inputs are greater than 0 which is not a risk to DivideByZeroException, hence try block won’t raise any exception and hence ‘except’ block won’t be triggered. And only when the control doesn’t flow to the except block, it flows to the else block. Further handling can be done inside this else block if there is something you want to do.
Example:
When an exception is raised, control flows into the except block and not the else block.
Output:
In the above code, As both the ‘b’ input is 0 which is a risk to DivideByZeroException, hence the ‘try’ block will raise an exception, and hence the ‘except’ block will be triggered. And now as there is an exception raised, the control flows to the except block and not to the else block.
Try Clause with Finally
Finally is a keyword that is used along with try and except, when we have a piece of code that is to be run irrespective of if try raises an exception or not. Code inside finally will always run after try and catch.
Example:
Where an exception is raised in the try block and except is triggered.
Output:
In the above code, we’re creating an array with 3 elements, i.e. max index up till 2. But when we try to access the 4th index, it will raise an exception of index out of range and will be caught in the except block. But here we need to observe that the finally block has also been triggered.
Example:
Rewriting the above code such that exception is not raised. Where an exception is not raised and else and finally both are triggered.
Note: else block will always be triggered before finally and finally will always trigger irrespective of any exceptions raised or not.
Output:
In the above code, we’re creating an array with 3 elements, i.e. max index up till 2. But when we try to access the 2nd index, now it will not raise an exception and control will now flow to another block and then to finally.
But here we need to observe that the finally block has also been triggered even though the exception was not raised.
Why Use Finally or Else in try..except?
- Consider above as a pseudo-code to a program where it checks out items added by the user in their cart.
- The Checkout method always creates an order id before booking all the items in the cart.
- Assuming itemsInCart contains all the items the user has added to a cart.
- In the checkout experience of an e-commerce website, an orderId is created irrespective of whether a booking of all items has been a success, failed, or partially failed.
- Hence in the above pseudo-code, we’re creating an orderId using GetOrderIdForBooking() and assume it returns a random string for each order. BookItems() books all the items for us in the itemsInCart array.
- If some error occurs while booking items, we have to log the reason for the exception LogErrorOccuredWhileBooking() does that job for us and if it's successful we have to log that the booking was successful.
- Now whatever the status of the order is, either way we have to email the status of the order to the user and that’s what the EmailOrderStatus() does for us.
The above example perfectly fits for an application of try, except or else and finally block as each block is playing a role that might’ve been difficult to achieve if we didn’t use these blocks.
What Happens if Errors are Raised in Except or Finally Block?
- If an error occurs while handling something in an except block, the finally block still gives us a chance to handle the situation our way.
Note: Once an exception/error is raised in the except block, finally block is triggered but the program still goes into a halt state post to that and the flow is broken. - But what if an error occurs in the finally block? Let’s orchestrate these series of events in the following code:
Output:
During handling of the above exception, another exception occurred:
During handling of the above exception, another exception occurred:
- Well, the finally block won’t be completed beyond the point where the exception is thrown. Note that except block won’t be triggered again.
Conclusion
- Exceptions can be expected or unexpected either way they are to be handled as the application goes into a halt state that most of the applications cannot afford
- Any code that is prone to an exception must be written in try block, if an exception is raised then the control goes into the except block - provided the except block accepts that specific type of exception or all exceptions in general.
- If no exception is raised then the code flows to the else block which is optional. Whether an exception is raised or not control always flows to the finally block which is also optional.
- Syntax in a nutshell:
- Hope this article gives you enough information on Exception Handling in Python.
- Handling exceptions is an important part of any application development as it enhances the quality of your end product.