Python: When To Use Custom Exceptions? Explained!

In this article, let us see when we should use an inbuilt exception and when we should use a custom exception.

For those of you in a hurry here is the short version of the answer.

Short Version of the Answer

A custom exception is the go-to in the following 3 situations

  • you wish to deal with something specific and particular in your code/software.
  • you want to include additional information when an exception is raised.
  • you want to improve code readability so that you can help you maintain your code among other benefits.

That being said, if there is already an inbuilt exception that already does the same job, it is better to stick with it!

This is to make sure you do not add unnecessary complexity to your code so that

  • the future users of your code will not have a hard time understanding it.
  • you don’t have to waste time and resources creating and designing custom exceptions.

To accomplish this you need to have the knowledge of what built-in exceptions that are available for you to utilize in specific situations. For that we have created a resource for your reference which you can find in the link below.

Python: Hierarchy of Exceptions

Don’t worry if the above answer does not make sense to you as that was targeted at more experienced programmers who just wanted to refresh their memories. The rest of this article is dedicated to those of you in the first steps of your journey to becoming a Python Craftsman.

By the time you reach the end of this article, I suggest you come back and read this summary once more, I assure you it will make much more sense then!

Custom vs Builtin Exceptions

I remember the time when I first came across code with custom exceptions. While the process of making a custom exception was fairly easy and straight forward, it left me with some questions,

  • When to make your own custom exception?
  • What exception to inherit the custom exception from?
  • What exceptions are already available as part of the standard Python programming language?

When I did the research to get these questions answered I found that the documentation available was fairly sparse, so I decided to write a series of articles explaining things I have learnt so that you do not have to waste time digging around in the internet like I did!

Lets start by looking at some available builtin exceptions.

When one should use built in exceptions

Python provides us with multiple exception classes that are designed to cover a lot of different situations or problems. All the available builtin exceptions are inherited from a few main Exception classes.

For example, the class ImportError will be raised when there’s a problem related to importing libraries:

import this_lib_does_not_exist

If you tried to mix two types or values that are not the same, a TypeError will be raised:

print(1+"a")

Any arithmetic or mathematically related problems will all come under the exception class ArithmeticError. For example

print(1/0)

will result in ZeroDivisionError, which itself is inherited from Arithmetic Error

The inbuilt classes are categorized into subclasses to be more specific in scenarios. For example, both ZeroDivisionError and OverflowError are a subclass of the ArithmeticError class. Yet they will be raised in different scenarios each:

#raises ZeroDivisionError
print(1/0)
#raises OverflowError 
print(10 ** 1000 / 10)

As you can see, Python already provides numerous classes to deal with exceptions. In situations such as these, where the inbuilt exceptions can already pin-point the underlying issue, then stick with that particular builtin exception.

Having an understanding of the hierarchy of the inbuilt classes will greatly help you as a developer when it comes to selecting which inbuilt exception you should select for your code, to save you time I have written another article on that topic, which you can find in the link below.

Python: Hierarchy of Exceptions

I also invite you to checkout our video on the top 7 most common exceptions in Python to get some ideas!

If you are using some 3rd party library, I suggest you read their documentation to understand any custom exceptions that they have created before you make your own custom exception.

Looking at things from another perspective, it’s especially best to stick with inbuilt exceptions when you’re building a software or an application that you’re going to share with others.

This is due to the following reasons

  • it make easier for others to use your library / extend your application
  • it makes it easier on yourself to create and maintain the code base
  • since these are well known and easy-to-research exceptions and
  • you don’t have to create a bunch of documentation to explain the users what a custom exception means and how to debug it

So, if you have an inbuilt exception that exists for your use case and seems perfect for the situation, stick to it!

When should you consider creating custom exception classes?

The standard builtin exceptions are generally sufficient since they cover a wide range of exceptions and problems one will encounter.

But sometimes the situation might call for something more than that, you might need a tailored exception for your program, you might need an exception that displays more information than what is currently being displayed, etc. And that’s when custom exceptions save the day!

Custom exceptions are fairly easy to construct, if you are not familiar on how to do this I suggest you read the article linked below.

Custom Exceptions in Python: Everything you need to know!

Since it is so easy to make a custom exception, there is always a temptation to make one! So, there is some “good judgement” needed from your end to make the decision if the given problem you are facing needs a custom exception or not!

The following problem is a good example:

Say you have a piece code which gets the age of a user as input to determine if a person is eligible to vote or not.

When we talk about age, we have 3 scenarios to deal with

  1. The age can be negative
  2. The age can be positive but less than 18
  3. The age can be greater than 18
def is_eligible():
    age = int(input("Please enter your age: "))
    if age <= 0:
        raise ValueError
    elif age < 18:
        print("You are not eligible to vote")
    else:
        print("You are eligible for voting!")

As you can see, the “negative age” issue is handled by raising a ValueError with a custom error message.

Then your teammate who is using the function you wrote can do something like this

age_not_ok = True

while (age_not_ok):
    try:
        is_eligible()
        age_not_ok = False
    except ValueError:
        print("Age cannot be less than 1")   

This will work just fine as shown in the output below.

Please enter your age: 0
Age cannot be less than 1

Please enter your age: -10
Age cannot be less than 1

Please enter your age: 20
You are eligible for voting!

We have handled the entire scenario using just inbuilt exceptions.

But this entire code was built on the assumption that the input form will only accept numbers.

So what happens when the user types the age using alphabets?

Please enter your age: twenty
Age cannot be less than 1

Please enter your age: 20
You are eligible for voting!

This caused the int() function to cause raise a ValueError which was then caught by our except clause.

As you can see the code still runs, but the prompt to the user does not make sense anymore!

The user said “twenty” and we complained that the user entered something less than 1!

This is where custom exception can come in handy!

We can modify the our function to differentiate between the 2 ValueErrors as shown below.

class InvalidAgeError(Exception):
    pass

def is_eligible():
    age = int(input("Please enter your age: "))
    if age <= 0:
        raise InvalidAgeError
    elif age < 18:
        print("You are not eligible to vote")
    else:
        print("You are eligible for voting!")

Then your teammate who is using the function you wrote can do something like this

age_not_ok = True

while (age_not_ok):
    try:
        is_eligible()
        age_not_ok = False
    except ValueError:
        print("Please use only the number keys 0..9")
    except InvalidAgeError:
        print("Your age cannot be less than 1! ")

which will then produce an output like this

Please enter your age: twenty
Please use only the number keys 0..9

Please enter your age: 0
Your age cannot be less than 1! 

Please enter your age: 20
You are eligible for voting!

Here to handle the issue of age being less than 1 we have created a custom exception called InvalidAgeError, which is then used by our teammate to differentiate between the 2 ValueErrors!

What is the advantage of creating your own custom exception class?

 By now you would’ve figured out some of the advantages of having a custom exception in your code but let’s go through them one by one:

Advantage#1: Increasing flexibility by adding more context

The option to add more attributes and methods is always one of the biggest advantages of using custom exceptions!

By doing this, you are not restricted and constricted to what an inbuilt exception can perform, you can add more stuff if you want.

This greatly enhances the flexibility of your exception to perform more tasks than what an inbuilt exception would generally do.

If you wish to learn what attributes and functions the Exception class in python offers, here is an excellent article for you!

“Exception” Class in Python: A Walk-through!

Advantage#2: Improved code readability

Custom exceptions can also help readability, provided you use meaningful names so that other developers can understand what is exactly happening along in the given context.

Advantage#3: Testing and Debugging

Since you would know the ins and outs of the custom exception that you have created. It will be far easier to write unit tests for your code. This way you can create test cases that only check for certain specific exceptions. 

The same way, custom exceptions also help with debugging by providing more information about the error if needed.

For instance, you can also include information such as stacktrace showing detailed information about where the exception occurred.

If you wish to learn how to do this, we have an excellent article on that topic linked below.

Python: Print StackTrace on Exception!

Though there are many advantages, like I mentioned in the beginning the main disadvantage is the added complexity, so do not go for custom exceptions unless absolutely necessary!

The rule of thumb is

When in doubt, use builtin exceptions!

embeddedinventor.com

And on that note, I will end this article!

Congratulations for making it till the end of the article, not many have the perseverance to do so!

I hope you enjoyed reading this article and found it useful!

Feel free to share it with your friends and colleagues!

If your thirst for knowledge have not been quenched yet, here are some related articles that might spark your interest!

Related Articles

Python: Details contained in an Exception

“Exception” Class in Python: A Walk-through!

Python: Hierarchy of Exceptions

Custom Exceptions in Python: Everything you need to know!

Thanks to Namazi Jamal for his contributions in writing this article!

Photo of author
Editor
Balaji Gunasekaran
Balaji Gunasekaran is a Senior Software Engineer with a Master of Science degree in Mechatronics and a bachelor’s degree in Electrical and Electronics Engineering. He loves to write about tech and has written more than 300 articles. He has also published the book “Cracking the Embedded Software Engineering Interview”. You can follow him on LinkedIn