Python Custom Exceptions



Custom Exception

Python had different built-in exceptions that are raised when something in a program went wrong.

In some cases, we may need to create our own custom exceptions that serve a specific purpose.


Creating Custom Exceptions

In Python, we can define custom exceptions by creating a new class. This exception class has to be derived directly or indirectly from the built-in Exception class.

Most of the built-in exceptions in Python are derived from the Exception class.

>>> class CustomError(Exception):
. . .        pass
. . .

>>> raise CustomError
Traceback (most recent call last)
...
CustomError: 


>>> raise CustomError("An error occurred")
Traceback (most recent call last)
...
CustomError: An error occurred

Here above, we have created a user-defined exception named CustomError that inherits from the Exception class. This new exception, like others exceptions, can be raised with the help of the raise statement that accepts an optional error message.

When our python program starts to grow up, it is a good practice to put all the user-defined exceptions in a separate file. Various standard modules do this. They generally define their exceptions separately as errors.py or exceptions.py.

A user-defined exception can implement everything a standard class can do. However, we generally make the user-defined exceptions concise and straightforward. Most implementations declare a custom base class and derive other exception classes from it. This approach is explained in the following example.


Example: User-Defined Exception in Python

In this example, we will see how user-defined exceptions can be used in a program to raise and catch errors.

In the following program, we will ask the user to keep entering a number until they guess the stored number correctly. To help the user figure out the stored number, we will provide a hint of whether their guess is greater than or less than the stored number.

# define Python user-defined exceptions
class BaseError(Exception):
    """Base class for other exceptions"""
    pass

class ValueTooSmallError(BaseError):
    """Raised when the input value is too small"""
    pass

class ValueTooLargeError(BaseError):
    """Raised when the input value is too large"""
    pass

# we need to guess this number
num = 9

while True:
    try:
        in_num = int(input("Enter a number:"))
        if in_num > num:
            raise ValueTooLargeError
        elif in_num < num:
            raise ValueTooSmallError
        break
    except ValueTooLargeError:
        print("This value is too large, try again!")
        print()
    except ValueTooSmallError:
        print("This value is too small, try again!")
        print()

print("Congratulation: You guessed it correctly.")

Here is an example run of the above program.

Enter a number:20
This value is too large, try again!

Enter a number:12
This value is too large, try again!

Enter a number:3
This value is too small, try again!

Enter a number:8
This value is too small, try again!

Enter a number:9
Congratulation: You guessed it correctly.

In the above code, we have defined a base class called BaseError.

The two exceptions ValueTooLargeError and ValueTooSmallError that are raised by the program are derived from the BaseError class. This is the common way to define user-defined exceptions, but we are not limited to this way.


Customizing Exception Classes

We can also customize a user-defined exception class to accept other arguments.

Let us see the following example:

class MarkNotInRangeError(Exception):
    """Exception raised for errors in the input mark
    
    Attributes
        mark -- input mark which caused the error
        message -- explanation of the error
    """
    def __init__(self, mark, message="Mark is not in (0, 100) range"):
        self.mark = mark
        self.message = message
        super().__init__(self.message)

mark = int(input("Enter mark of student: "))
if not 0 <= mark <= 100:
    raise MarkNotInRangeError(mark)

Output

Enter mark of student: 150

Traceback (most recent call last)
---> 15     raise MarkNotInRangeError(mark)
MarkNotInRangeError: Mark is not in (0, 100) range

In the above code, we have overridden the constructor of the Exception class to accept our own custom arguments mark and message. Then, the constructor of the parent Exception class is called manually with the self.message argument using super().

The self.mark attribute is defined to be used later.

When the MarkNotInRangeError is raised, implicitly, the inherited __str__ method of the Exception class is then used to display the corresponding message.

We can also customize the __str__ method itself by overriding it.

class MarkNotInRangeError(Exception):
    """Exception raised for errors in the input mark
    
    Attributes
        mark -- input mark which caused the error
        message -- explanation of the error
    """
    def __init__(self, mark, message="Mark is not in (0, 100) range"):
        self.mark = mark
        self.message = message
        super().__init__(self.message)

    def __str__(self):
        return f'{self.mark} -> {self.message}'

mark = int(input("Enter mark of student: "))
if not 0 <= mark <= 100:
    raise MarkNotInRangeError(mark)

Output

Enter mark of student: 123

Traceback (most recent call last)
---> 18     raise MarkNotInRangeError(mark)
MarkNotInRangeError: 123 -> Mark is not in (0, 100) range

To learn more about how to handle exceptions in Python, you can visit Python Exception Handling.



ExpectoCode is optimized for learning. Tutorials and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using this site, you agree to have read and accepted our terms of use, cookie and privacy policy.
Copyright 2020-2021 by ExpectoCode. All Rights Reserved.