Skip to main content

Python 错误处理

Python 错误处理高级编程

在掌握了 Python 错误处理的基础知识后,我们可以进一步探索更高级的技巧。这些技巧能够帮助你编写更灵活、更可维护的代码。本文将深入讲解自定义异常类、异常的嵌套与链式处理、抛出异常、上下文管理器以及日志记录与错误跟踪。

错误处理的设计原则

有效的错误处理应遵循以下原则:

  • 明确性:捕获特定异常,避免使用通用的except Exception。
  • 最小化:仅在必要时捕获异常,避免过度使用try-except。
  • 可读性:提供清晰的错误消息,便于用户和开发者理解。
  • 可恢复性:设计程序以从错误中恢复或优雅退出。

自定义异常类

当内置异常类型无法满足特定需求时,可以通过继承Exception类创建自定义异常。自定义异常可以携带额外信息,使错误处理更具语义化。

以下是一个定义和使用自定义异常的示例:

class InvalidInputError(Exception):
    def __init__(self, message, value):
        self.message = message
        self.value = value
        super().__init__(self.message)

def validate_number(num):
    if not isinstance(num, (int, float)) or num < 0:
        raise InvalidInputError("Input must be a non-negative number", num)
    return num * 2

try:
    result = validate_number(-5)
    print(f"Result: {result}")
except InvalidInputError as e:
    print(f"Error: {e.message}, Invalid value: {e.value}")

输出:

Error: Input must be a non-negative number, Invalid value: -5

在这个例子中,InvalidInputError类增加了value属性来存储无效输入,便于调试和错误报告。自定义异常让代码更具可读性和针对性。

异常的嵌套与链式处理

在复杂程序中,异常可能在多个层次中发生。嵌套try-except块可以处理不同层次的错误,而异常链(通过raise ... from)可以保留原始异常的上下文。

以下是一个展示嵌套异常和异常链的示例:

class DatabaseError(Exception):
    pass

def fetch_data(query):
    if not query:
        raise ValueError("Query cannot be empty")

def process_query(query):
    try:
        fetch_data(query)
    except ValueError as e:
        raise DatabaseError("Failed to process query") from e

try:
    process_query("")
except DatabaseError as e:
    print(f"Error: {e}")
    print(f"Caused by: {e.__cause__}")

输出:

Error: Failed to process query
Caused by: Query cannot be empty

在这个例子中,fetch_data抛出ValueErrorprocess_query捕获后抛出DatabaseError,并通过from保留了原始异常信息。e.__cause__显示了异常链的根源,便于追溯问题。

使用raise抛出异常

raise语句可以主动抛出异常,用于在特定条件下中断程序或传递错误信息。你可以抛出内置异常或自定义异常。

以下是一个主动抛出异常的示例:

def divide_with_limit(a, b, limit):
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    result = a / b
    if result > limit:
        raise ValueError(f"Result {result} exceeds limit {limit}")
    return result

try:
    result = divide_with_limit(100, 2, 40)
    print(f"Result: {result}")
except (ZeroDivisionError, ValueError) as e:
    print(f"Error: {e}")

输出:

Error: Result 50.0 exceeds limit 40

这个例子展示了如何在特定条件下(如结果超过限制)抛出异常。使用元组(ZeroDivisionError, ValueError)可以同时捕获多种异常类型。

上下文管理器与错误处理

上下文管理器(通过with语句使用)可以简化资源管理和错误处理,特别是在处理文件、数据库连接等需要清理的资源时。contextlib模块提供了创建自定义上下文管理器的工具。

以下是一个使用上下文管理器处理文件操作的示例:

from contextlib import contextmanager

@contextmanager
def safe_file_writer(filename):
    file = None
    try:
        file = open(filename, 'w')
        yield file
    except Exception as e:
        print(f"Error writing to {filename}: {e}")
    finally:
        if file:
            file.close()
            print(f"File {filename} closed")

try:
    with safe_file_writer("output.txt") as f:
        f.write("Hello, World!")
        raise ValueError("Simulated error")
except ValueError as e:
    print(f"Caught error: {e}")

输出:

Error writing to output.txt: Simulated error
File output.txt closed

在这个例子中,safe_file_writer上下文管理器确保文件在写入错误后也能正确关闭。yield将控制权交给with块,finally保证资源清理。

日志记录与错误跟踪

在生产环境中,仅仅打印错误信息不足以调试复杂问题。Python的logging模块可以记录详细的错误信息,包括时间戳、错误级别和堆栈跟踪。

以下是一个使用logging记录错误的示例:

import logging

logging.basicConfig(
    filename='app.log',
    level=logging.ERROR,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def process_data(data):
    try:
        result = 100 / data
        return result
    except ZeroDivisionError as e:
        logging.error("Division by zero error", exc_info=True)
        return None

result = process_data(0)
if result is None:
    print("Operation failed, check log for details")

输出(控制台):

Operation failed, check log for details

输出(app.log文件):

2025-07-24 10:29:36,682 - ERROR - Division by zero error
Traceback (most recent call last):
  File "/Users/yzy/programs/hello_py312/main.py", line 11, in process_data
    result = 100 / data
             ~~~~^~~~~~
ZeroDivisionError: division by zero

这个例子使用logging.error记录异常的完整堆栈信息,exc_info=True确保包含追溯信息。日志文件便于长期跟踪和分析错误。

总结

本篇文章我们学习了Python高级错误处理技巧,包括:

  • 创建和使用自定义异常类
  • 处理嵌套异常和异常链
  • 主动抛出异常以控制程序流程
  • 使用上下文管理器简化资源管理和错误处理
  • 使用logging模块记录详细的错误信息