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.
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:
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"
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.
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 exceptionexcept:# 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 Exceptionexcept 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:
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 exceptionexcept BufferError as e:# What to do about the caught BufferErrorexcept 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_index =5if(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).
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.
classMyBaseErrorClass(Exception):# This is the user-defined base classpassclassMyErrorOne(MyBaseErrorClass):# This is a user-defined child class# derived from the user-defined base classdef__init__(self, my_value):
self.my_value = my_value
def__str__(self):return(repr(self.my_value))classMyErrorTwo(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.
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:
classMyFileReaderLib(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:
try:# some instruction that will raise an errorexcept Exception as e:
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.
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.