Python Exception Handling: Exploration and Best Practices of Three Different Approaches

PythonException handling: exploration and best practices of three different methods

Foreword

This article aims to explore three different exception handling methods in Python. By having a deep understanding of various exception handling strategies, we can better deal with different programming scenarios and choose the method that best suits our needs.

Exception handling plays a vital role in programming. Proper exception handling can not only improve the robustness of the code, but also enhance the readability and maintainability of the program. In Python programming, effectively managing exceptions is a key part of improving code quality.

Before we dive in, let’s introduce the topic through a practical programming challenge:

The day before yesterday, a friend asked me a question. When dealing with a loop traversal, since it is difficult to foresee all possible errors, he needs to implement exception handling for each element in the loop to prevent an error in a certain element from affecting the operation of the entire program.

But the result of this is that the code becomes verbose and difficult to maintain due to too many try-except blocks. In this case, how can we optimize the code to handle exceptions while keeping the code clear and concise?

The sample code is as follows:

for item in html_xpath:
    try:
        try:
url = item.xpath('//title/url-ellipsis/a/url()')
        except Exception as e:
            url = None
        try:
            title = item.xpath('//title/text-ellipsis/a/text()')
        except Exception as e:
            title=None
        ...
    except Exception as e:
        ...

In this article, we will explore three different methods of exception handling and return to the problem at the end to provide an optimized solution.

Knowledge points

Check out these two articles, it will be more helpful for you to consume this article! !

  • An in-depth introduction to Python exception handling – Python exceptions you don’t know

  • A long article of 10,000 words – Basic configuration of Python logger logging encyclopedia

Here we first summarize the advantages, disadvantages and application scenarios of the three exception handling methods introduced below:

Method Advantages Disadvantages Application scenarios
try-except block Simple, direct and easy to understand.
Specific processing logic can be written for different types of exceptions.
Frequent use in code will lead to verbose code Suitable for handling errors that are known to occur.
Used for error handling within specific functions or code blocks.
sys.excepthook Catch unhandled exceptions globally.
Relatively simple to use.
Cannot prevent the program from terminating due to exception.
Only handle exceptions that are not caught by a try-except block.
Not applicable in child threads.
Suitable for logging uncaught exceptions.
Error reporting and logging.
Decorators Improving code reusability and clarity.
Customizable exception handling logic.
Using and understanding requires a higher level of Python skill.
Applies only to decorated functions.
Applicable to functions that require unified exception handling logic.
Used to reduce code duplication and improve maintainability.

Summary of exception handling methods

There are many ways to handle exceptions in Python, each suitable for different situations and needs.

By choosing appropriate exception handling methods, we can better manage and handle exceptions in Python programs.

Here are three common exception handling methods and their advantages and disadvantages:

1. Use try-except blocks

Advantages: Simple, direct and easy to understand; allows writing specific processing logic for different types of exceptions.

Disadvantages: Frequent reuse in code can lead to verbose code.

Sample code:

try:
    # Code that may cause exceptions
    result=1/0
except ZeroDivisionError:
    # Handle specific types of exceptions
    print("Cannot divide by zero")
    

Code explanation:

The code uses a try-except block to catch a specific type of exception (ZeroDivisionError) and prints an error message.

The code runs as follows:

2. Use sys.excepthook

sys.excepthook is a global function in Python that is called when the script encounters an uncaught exception. By default, when an exception is not caught by any try-except block, Python will call sys.excepthook, printing the exception information and Stack trace.

Advantages: Allows catching unhandled exceptions anywhere in the program; relatively simple to use.

Disadvantages: Does not prevent program termination due to unhandled exceptions; can only be used to handle exceptions not caught by try-except blocks.

Sample code:

import sys


def global_exception_handler(exc_type, exc_value, exc_traceback):
    print("It’s done! The exception is caught here:", exc_value)


sys.excepthook = global_exception_handler

# Example: Deliberately creating a divide-by-zero error
result=1/0
print('It didn’t run to this point!')

Code explanation:

The code configures sys.excepthook so that when an uncaught exception occurs, it will call the global_exception_handler function to handle the exception.

It allows catching unhandled exceptions anywhere in the program, but the program will terminate (gracefully exit) after catching the unhandled exception.

The code runs as follows:

  • As you can see, the code does not run to the line print('It didn’t run to this point!')~

3. Use decorators

Advantages: Improve code reusability and clarity, and reduce duplicate code; exception handling logic can be customized and applied to specific functions.

Disadvantages: Use and understanding of decorators requires a higher Python skill level than direct try-except blocks; only applicable Decorated function.

Sample code:

def catch_exceptions(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"Exception caught in {<!-- -->func.__name__}: {<!-- -->e}")
            return None

    return wrapper


@catch_exceptions
def risky_function(x, y):
    return x/y


result = risky_function(1, 0)
print("Program continues execution")

Code explanation:

The code defines a decorator catch_exceptions that can be applied to all functions that need to be handled. When the decorated function throws an exception, the decorator catches the exception and prints an error message.

The code runs as follows:

  • It can be seen that after catching the exception, the program can still execute normally~

4. More robust code

This code adds stack printing and logging based on Using decorators. The role of logging must be clear to everyone.

Regarding the use of logging, you can check my previous article.

import logging
import traceback
importsys

# Configure logger
logging.basicConfig(
    filename='app.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    encoding='utf-8',
    filemode='w'
)


def catch_exceptions(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            #Record exception information to the log
            logging.error(f"Exception caught in {<!-- -->func.__name__}: {<!-- -->e}")
            logging.error(f"Exception type: {<!-- -->exc_type}")
            logging.error(f"Exception value: {<!-- -->exc_value}")
            log_traceback = ''.join(traceback.format_tb(exc_traceback))
            logging.error(f"Exception traceback: {<!-- -->log_traceback}")
            return None

    return wrapper


@catch_exceptions
def risky_function(x, y):
    return x / y # ZeroDivisionError may be raised here


result = risky_function(1, 0)
print("Program continues execution")

The code runs as follows:

  • As you can see, the information recorded in the log is very clear.

Solve the previous problem

This code solves the previous problem, nice~

In this code, I simulated a piece of html text, and then specifically used the wrong expression in xpath_expression.
Since the code is only for demonstration, logging and battle stack will not be added here~~

from lxml import html


#Define exception handling decorator
def catch_exceptions(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"Exception caught in {<!-- -->func.__name__}: {<!-- -->e}")
            return 'empty'

    return wrapper


# Use decorators to parse HTML elements
@catch_exceptions
def parse_element(sub_element, xpath_expression):
    return sub_element.xpath(xpath_expression)


# Sample HTML element
item_html = """
<div>
    <a href="https://frica.blog.csdn.net/?type=blog">frica Link</a>
    <span>It’s a side dish</span>
    ...
</div>
"""

# Define the mapping between HTML elements and XPath
html_xpath_map = {<!-- -->
    'url': "//a/@href",
    'title': "//span/text()",
    'other': '//dd/dd/ddd/text()',
    'age': 'This is not an xpath_expression'
}

if __name__ == '__main__':
    result_map = dict()

    #Create HTML element object
    element = html.fromstring(item_html)

    # Traverse the XPath mapping, parse the elements and store the results in the dictionary
    for key, value in html_xpath_map.items():
        result = parse_element(element, value)
        result_map[key] = result[0] if result else 'empty'
        #walrus operator
        # result_map[key] = x[0] if (x := parse_element(element, value)) else 'empty'

    #Print parsing results
    print(result_map)

Code explanation:

The main purpose of this code is to solve the problem of handling exceptions during loop traversal.

Simplify exception handling and make your code clearer and concise by using decorators and Xpath.

Overall, this code for exception handling is already very robust! !

Readers who don’t understand, please go back and read my previous article~~

Code running effect:

Summary

In Python, different exception handling methods are suitable for different scenarios.

  • The try-except block is suitable for handling errors that are known to occur, and is suitable for error handling in specific functions or code blocks.
  • sys.excepthook is suitable for recording uncaught exceptions and is used for error reporting and logging, but it cannot prevent program termination.
  • Decorators are suitable for functions that need to unify exception handling logic to improve code reusability and clarity.

When choosing an exception handling method, you should consider which method to use based on specific needs and project background, and write exception handling code according to best practices and considerations to ensure the robustness and maintainability of the code.

Afterword

This sharing ends here,

see you~