A Complete Guide to Python Exceptions
A Python program will terminate as soon as it encounters an error.
These errors can be syntax errors or they can be exceptions
. An exception is an event that interrupts the normal flow of your Python program.
This article will teach you how to handle Python exceptions using try
and except
statements.
Table of Contents
You can skip to a specific section of this Python exception tutorial using the table of contents below:
- Introduction to Python Exceptions
- Python Exception Hierarchy
- Raising Custom Exceptions
- User-Defined Exceptions
- Python Exception Wrapping
- Python Exception Logging
- Else and Finally Statements
- Final Thoughts
Introduction to Python Exceptions
There are many errors that can happen in computer programs. Broadly speaking, they can be divided into compiler errors and runtime errors.
As their names suggest, compiler errors occur at the compile-time and runtime errors occur while the program is running. Although the former can be identified, generally, runtime errors can only be found when the program throws an exception and crashes.
If the programs are to run smoothly without any interruptions, languages need to have a mechanism to address this. This is why exceptions exist!
Take a look at the following Python code to see an example of an exception.
my_list = [0, 1, 2, 3]
print(my_list[5])
If we try to execute this code, Python will throw the following error.
IndexError: list index out of range
The reason this exception is thrown is because we're trying to reference an item from our Python list that doesn't exist. my_list
contains four elements, so its highest index is 4
(since Python is zero-indexed).
Had this piece of code been in a bigger program, once it reaches this point, it would throw this error and stop its execution. If this had happened in a packaged program already running in an end user’s device, this would be a disaster. The program would crash every time it encounters this error.
This is why exception handling is so important. It is an essential tool that allows programming languages to deal with runtime errors. Exceptions prevent the execution of the program from being terminated.
To see this concept in action, let's add a few lines to this code:
my_list = [0, 1, 2, 3]
try:
print(my_list[5])
except:
print(100)
This program would give 100 as its output.
If we take a look at the code, we can see that the original print statement is placed in a try block. There is also an except block added with a print statement.
In simple terms, this means the script will try to print the 5th index of my_list
. If it does not succeed, it will print 100 instead. Note that we have not specified what types of errors should be caught.
In Python, the try block is always followed by at least one except
statement. You can use multiple except
define what errors you want to catch and how you want your program to respond to them. This allows your machine to handle different exceptions in different ways.
In Python, the most popular and widespread class for exception handling is the Exception
class. Python developers can also create user-defined exceptions. This Exception
class is usually the parent from which user-defined exception classes are derived from. The Exception
class itself even has a parent class and sibling classes.
We'll consider the hierarchy of Python exceptions in the next section of this tutorial.
Python Exception Hierarchy
The hierarchy of Python exception classes is based on the BaseException
class. All exception instances or objects are derived from this BaseException
class or its child classes. BaseException
has four immediate child classes"
SystemExit
GeneratorExit
KeyboardInterrupt
Exception
Catching Exceptions
Exceptions exist so that developers can catch them and decide how to respond to them.
If the program might potentially throw many types of errors, they can either be addressed as a whole, in categories, or even individually.
The following is a general guide with examples on how to deal with the exceptions.
Catch All
Catching all exceptions is not advised for complex applications. However, for basic Python scripts, it can be useful to react to every exception in the same way.
Here is the basic syntax for a catch all exception:
try:
# An instruction that might likely
# throw an exception
except:
# What to do about it
Reacting to Specific Exceptions
This is the most common method for exception handling in Python. It allows you to react to specific
Here's the syntax you'd use to react to an exception from the Exception
class. You'd use similar logic for any other child or sibling class.
try:
# An instruction that might likely
# throw an exception but preferably
# not a from a sibling class of Exception
except Exception:
# What to do about it
Catching Multiple Exceptions
Multiple except statements can be chained together to catch different exceptions and handle them individually. The syntax is for one link is this chain is below:
except YourErrorHere:
Here's a longer example of how you could handle longer chains of exceptions in a Python script:
try:
# An instruction that might likely
# throw an exception
except BufferError as e:
# What to do about the caught BufferError
except ArithmeticError as e:
# What to do about the caught ArithmeticError
Raising Custom Exceptions
Python offers lots of flexibility when it comes to raising exceptions. Developers can even instruct programs to raise built-in exceptions in addition to the user-defined ones. This comes handy when performing unit testing with exception handlers and specific conditions in a program.
Users can also raise warning messages in addition to exceptions. Warnings are useful in informing or altering the users about the program in scenarios where you don't want to terminate the program, such as:
- the use of outdated modules
- reaching allowed memory limits
- using a method that is often used incorrectly
Tt is the sole responsibility of the user to react to these warnings. There is no mechanism to regulate what exceptions or warnings can or cannot be raised by the user.
As an example, the following code.
my_list = [0, 1, 2, 3]
my_list_index = 5
if(my_list_index>len(my_list)):
raise Exception("Index is out of range. Use a value less than 4")
The above code would generate:
Exception: Index is out of range. Use a value less than 4
This raise
statement gives developers the ability to force exceptions at different points of their code. These exceptions must be fulfilled with an instance of BaseException
, or one of the derived classes (like Exception
, which I used in the example above).
User-Defined Exceptions
Python enables developers to define custom exception classes. They are usually derived from the Exception
class. They behave in the same way as the other Exception
-based classes do.
As a rule of thumb, exception classes should be kept as simple as possible with just enough attributes to identify the potential error to be caught. Moreover, a main base class should be created that extends the Exception class. Other subclasses should be derived from that user-defined base class depending on the error conditions.
Let's consider an example.
class MyBaseErrorClass(Exception):
# This is the user-defined base class
pass
class MyErrorOne(MyBaseErrorClass):
# This is a user-defined child class
# derived from the user-defined base class
def __init__(self, my_value):
self.my_value = my_value
def __str__(self):
return(repr(self.my_value))
class MyErrorTwo(MyBaseErrorClass):
def __init__(self, my_value, my_user_name):
self.my_value = my_value
self.my_user_name = my_user_name
def __str__(self):
return('user ' + self.my_user_name + ' entered ' + str(self.my_value))
We can test what happens if the error gets caught as follows.
raise(MyErrorTwo(2,'David'))
This generates:
MyErrorTwo: user David entered 2
Python Exception Wrapping
In exception wrapping, wrapper classes are written for exceptions to wrap one exception in another. This comes in handy when there are collaborative implementations of libraries.
For instance, consider a scenario in which a file reader library internally uses another 3rd party library called SomeLib
to acquire files from Google Drive. In case the expected file is missing from Google Drive, let’s assume that it throws a somelib.exceptions.wronglink
error. Since the user doesn't know this, there could be issues if exceptions are thrown by the internal library. You can address this issue by wrapping the exception.
Here's an example of the implementation of this:
class MyFileReaderLib(Exception):
def __init__(self, msg, somelib_exception):
super(MyFileReaderLib, self).__init__(msg + (": %s" % somelib_exception))
self.somelib_exception = somelib_exception
def getFileOnline:
try:
return somelib.getfile('drive.google.com/u/2akjhaADS')
except somelib.exceptions.wronglink as exep:
raise MyFileReaderLib('Link is wrong or file not found', exep)
Python Exception Logging
Logging exceptions is just as important as catching exceptions. Logging simply means that imformation about exceptions should be logged for future reference - whether it be for debugging, postmortems, or anything else. You can use Python's logging
library to accomplish this.
Here's a straightforward implementations of logging in Python:
import logging
try:
# some instruction that will raise an error
except Exception as e:
logging.exception("message")
The instruction inside the except
statement will produce a stack trace with the message. Note that the logging.exception
method should only be used inside the except block. If it is used elsewhere, it might produce unexpected results (namely a tremendous amount of necessary log entries).
Else and Finally Statements
Adding an else
statement to try-except
statements broadens their functionality. More specifically, the else
statement allows you to specify what should be done if no exceptions are raised.
Consider the following code as an example:
my_list = [0, 1, 2, 3]
try:
print(my_list[3])
except:
print(100)
else:
print('No errors')
This generates:
3
No errors
By adding a finally
statement to the try-except block, the program can perform a final task after executing the tasks inside the except block.
my_list = [0, 1, 2, 3]
try:
print(my_list[5])
except:
print(100)
else:
print('No errors')
finally:
print('Just exiting try-block')
The above code generates:
100
Just exiting try-block.
Final Thoughts
Exceptions handling is used in programs to ensure that the flow of execution is not interrupted with any raised errors.
Exceptions are essential components of building robust Python applications. In the event any errors are raised, the program will follow the instructions defined in its except
statements.
This tutorial serves as a broad introduction to exception handling in Python. While exception handling is a broad and complex topic, you should now have the information required to add your first exception management to your Python projects.