DEV Community

Cover image for Exception Handling in Python
Alex Ricciardi
Alex Ricciardi

Posted on

Exception Handling in Python

This article explores the various techniques used to handle exceptions in Python, including try-except blocks, custom exceptions, and advanced features like exception chaining and enrichment.


Python provides a robust exception-handling framework that not only allows programmers to implement code that prevents crashes but also offers feedback and maintains application stability. Moreover, it enables developers to manage errors gracefully using constructs like try-except blocks, custom exceptions, and more.

- The Try-Except Block

In the try-except block, the code that may raise an exception is placed in the try-block, and the except-block specifies the actions to take if an exception occurs (Python Software Foundation, n.d.).

For example:

try:
    result = 1 / 0
except ZeroDivisionError:
    print("Cannot divide by zero.")
Enter fullscreen mode Exit fullscreen mode

To catch multiple exceptions in one try-except block, we can use a try-block with several except-blocks to generate specific responses for each exception type. Or, we can use a tuple to catch multiple exceptions with a single exception expression.

For example:

# One try block and several except blocks
try:
    result = 1 / 'a'
except ZeroDivisionError:
    print("Cannot divide by zero.")
except TypeError:
    print("Type error occurred.")

# One try block and one except tuple block
try:
    # some operation
    result = 1 / 'a'
except (ZeroDivisionError, TypeError) as e:
    print(f"Error occurred: {e}")
Enter fullscreen mode Exit fullscreen mode

- The Else Clause

The else clause, is placed after the try-except blocks and runs if the try block does not raise an exception.

For example:

try:
    result = 1 / 2
except ZeroDivisionError:
    print(Cannot divide by zero.)
else:
    print(Division successful.)
Enter fullscreen mode Exit fullscreen mode

- The Finally Clause

The finally clause is always placed after the try-block or any except-block. It contains code that runs no matter what, typically for cleaning up resources like files or network connections, even if an exception was raised.

For example:

try:
    result = 1 / a
except ZeroDivisionError:
    print(Cannot divide by zero.)
except TypeError:
    print(Type error occurred.)
else:
    print(Division successful.)
finally:
    print(Goodbye, world!”)
Enter fullscreen mode Exit fullscreen mode
  • The Raise Statement

Raising exceptions: the raise clause raises exceptions by forcing an exception to occur, usually to indicate that a certain condition has not been met.

For example:

if a > 5:
    raise ValueError(A must not exceed 5)
Enter fullscreen mode Exit fullscreen mode

- Exception Chaining

You can chain exceptions with the clause raise. This is useful for adding context to an original error.

For Example

try:
    open(myfile.txt)
except FileNotFoundError as e:
    raise RuntimeError(Failed to open file) from e
Enter fullscreen mode Exit fullscreen mode

- Custom exceptions

You can define your own exception classes by inheriting from the Exception class or any other built-in exception class (Mitchell, 2022).

For example:

class My_custom_ (Exception):
    pass

try:
    raise MyCustomError(An error occurred)
except MyCustomError as e:
    print(e)
Enter fullscreen mode Exit fullscreen mode

- Enriching exceptions

you can add information or context to an exception by using the add_note() method to ‘append’ custom messages or notes to the exception object aka e.

For example:

def divide_numbers(a, b):
    try:
        result = a / b
    except ZeroDivisionError as e:
        e.add_note(Cannot divide by zero)
        e.add_note(Please provide a non-zero divisor)
        raise
try:
    num1 = 10
    num2 = 0
    divide_numbers(num1, num2)
except ZeroDivisionError as e:
    print(An error occurred:)
    print(str(e))
Enter fullscreen mode Exit fullscreen mode

Handling exceptions is important for several reasons:

  1. Prevents program crashes: Unhandled exceptions can cause the program to crash, leading to data loss and a poor user experience.
  2. Provides meaningful error messages: By handling exceptions, you can provide users with informative error messages that help them understand what went wrong and how to fix it.
  3. Allows for graceful degradation: Exception handling enables the program to continue running even if an error occurs.

A simple program error handling example:

##-------------------------------------------
# Pseudocode:
# 1. Define a custom exception class called CustomError.
# 2. Create a function that raises the CustomError exception 
#    based on a condition.
# 3. Use a try-except block to handle the CustomError exception.
#-------------------------------------------
# Program Inputs:
# - num: An integer value.
#-------------------------------------------
# Program Outputs:
# - Custom exception message when the CustomError exception is raised.
# - Success message when no exception is raised.
#-------------------------------------------

class CustomError(Exception):
    '''
        A custom exception class.
    '''
    pass
def check_number(num):
    '''
        Checks if the given number is positive.
        :param int: num, an integer value.
        :raises CustomError: If the number is not positive.    :Return: None
    '''
    if num <= 0:
        raise CustomError("Number must be positive.")
    print("Number is valid.")
#--- Main Program
def main() -> None:
    '''
        The main function that demonstrates the usage of custom exceptions.
    '''
    try:
        check_number(5)  # Valid number
        check_number(-2)  # Raises CustomError
    except CustomError as e:
        print(f"Error: {str(e)}")
#--- Execute the program
if __name__ == "__main__": main()
Enter fullscreen mode Exit fullscreen mode
>>>
Number is valid.
Error: Number must be positive.
Enter fullscreen mode Exit fullscreen mode

To summarize, Python provides a comprehensive exception-handling framework that allows programs to handle unexpected situations without failing abruptly. By utilizing constructs such as try-except blocks, custom exceptions, and advanced features like exception chaining and enrichment, developers can ensure that their programs are resilient, user-friendly, and capable of handling unexpected scenarios gracefully.


References:

Mitchell R (2022, June 13). Custom exceptions. _Python Essential Training _[VIDEO]. LinkedIn Learning. https://www.linkedin.com/learning/python-essential-training-14898805/custom-exceptions?autoSkip=true&resume=false&u=2245842

Python Software Foundation. (n.d.). 8. Errors and Exceptions. Python. python.org.


Originally published at Exception Handling in Python - Medium on August 21, 2024.

Top comments (0)