脚本专家 发表于 2026-7-20 12:00:00

Python异常处理实战:try-except到自定义异常类

Python的异常处理是编写健壮代码的核心技能。本文从实际案例出发,系统讲解try-except、异常层级、主动抛出异常、自定义异常及异常传递等知识点,并给出最佳实践和常见陷阱。

一、从文件读取错误看异常处理

假设我们要读取一个文件:

file_path = "data.txt"
with open(file_path, 'r') as f:
    content = f.read()
    print("文件内容:", content)


如果data.txt不存在,程序会直接崩溃并抛出FileNotFoundError。异常(Exception)是运行时发生的错误事件,会中断正常流程。没有异常处理,程序遇到错误就终止,用户体验极差。

二、Python内置异常的层级关系

所有异常都继承自BaseException。主要层级如下:
- BaseException
- SystemExit
- KeyboardInterrupt
- GeneratorExit
- Exception(绝大多数业务异常在此)
    - StopIteration
    - ArithmeticError (FloatingPointError, OverflowError, ZeroDivisionError)
    - AssertionError
    - AttributeError
    - EOFError
    - ImportError (ModuleNotFoundError)
    - LookupError (IndexError, KeyError)
    - NameError (UnboundLocalError)
    - OSError (FileNotFoundError, PermissionError, TimeoutError)
    - RuntimeError (NotImplementedError)
    - SyntaxError (IndentationError)
    - TypeError
    - ValueError (UnicodeError)
    - 以及其他Warning等

理解层级关系有助于精确捕获。例如捕获OSError可以同时处理FileNotFoundError和PermissionError。

三、try...except的三种捕获方式

基础语法:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("除数不能为零!")
except (TypeError, ValueError) as e:
    print(f"类型或值错误:{e}")
except Exception as e:
    print(f"发生未知错误:{e}")


建议优先捕获具体异常,避免笼统捕获Exception。

四、获取异常详细信息的四种方法

通过traceback模块可以获取完整栈信息:

import traceback
try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(f"异常对象:{e}")                     # 1
    print(f"异常类型:{type(e).__name__}")      # 2
    print(f"完整信息:{traceback.format_exc()}")# 3
    print(f"异常参数:{e.args}")                # 4


五、try-except-else-finally完整流程


def read_file_safely(filename):
    content = None
    try:
      print(f"尝试打开文件:{filename}")
      with open(filename, 'r') as f:
            content = f.read()
    except FileNotFoundError:
      print(f"文件 {filename} 不存在")
    except PermissionError:
      print(f"没有权限读取文件 {filename}")
    except OSError as e:
      print(f"系统错误:{e}")
    else:
      print("文件读取成功!")
      return content
    finally:
      print("文件操作结束,清理完成")
      return content# 注意:finally中的return会覆盖else中的return

result = read_file_safely("data.txt")


执行顺序:try → 命中except → else(无异常时) → finally。注意finally块中的return会覆盖前面的返回值,这是常见陷阱。

六、使用raise主动抛出异常


def validate_age(age):
    if not isinstance(age, int):
      raise TypeError("年龄必须是整数")
    if age < 0:
      raise ValueError("年龄不能为负数")
    if age > 150:
      raise ValueError("年龄不能超过150岁")
    return True

try:
    validate_age(-5)
except ValueError as e:
    print(f"验证失败:{e}")


raise可以附带自定义消息,也可在except块中重新抛出异常。

七、自定义异常类

继承Exception,添加业务属性:

class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
      self.balance = balance
      self.amount = amount
      message = f"余额不足!当前余额:{balance},需要:{amount}"
      super().__init__(message)

class BankAccount:
    def __init__(self, balance):
      self.balance = balance

    def withdraw(self, amount):
      if amount > self.balance:
            raise InsufficientFundsError(self.balance, amount)
      self.balance -= amount
      return self.balance

account = BankAccount(1000)
try:
    account.withdraw(1500)
except InsufficientFundsError as e:
    print(f"取款失败:{e}")
    print(f"余额={e.balance}, 请求={e.amount}")


自定义异常使错误语义更清晰,便于调用方进行精确处理。

八、异常的传递机制

异常未捕获时会沿调用栈向上传递:

def function_a():
    print("函数A开始")
    function_b()
    print("函数A结束")# 不会执行

def function_b():
    print("函数B开始")
    function_c()
    print("函数B结束")# 不会执行

def function_c():
    print("函数C开始")
    raise ValueError("函数C中发生错误")
    print("函数C结束")# 不会执行

try:
    function_a()
except ValueError as e:
    print(f"在最外层捕获到异常:{e}")
    print("传递路径:function_c → function_b → function_a → 主程序")


异常传递特点:从发生点向上传播,每层都可选择捕获或继续抛出;若最终无捕获,程序崩溃并显示完整调用栈。

九、最佳实践与常见陷阱

最佳实践:
- 优先捕获具体异常类
- 避免空except:(会捕获KeyboardInterrupt等)
- 在生产环境记录异常详情
- 使用finally或with确保资源释放
- 为业务逻辑创建自定义异常

常见陷阱:
1. 过于宽泛的捕获:except: 会吞掉SystemExit等
2. 吞掉异常信息:直接pass而不记录
3. finally中return覆盖try/else中的return值

十、总结

异常处理不是消灭所有错误,而是优雅地处理预期内的异常。掌握try-except-else-finally、异常层级、raise、自定义异常及传递机制,能显著提升代码的健壮性和可维护性。

热心网友4 发表于 2026-7-20 12:05:00

Re: Python异常处理实战:try-except到自定义异常类

感谢楼主分享这么详细的异常处理教程!从基础语法到自定义异常,内容非常系统,特别是用文件读取作为切入点,特别贴近实际开发。我有两个地方特别有共鸣: 1. 你对`finally`中return会覆盖其他return的提醒很关键,这个陷阱我早期踩过很多次。 2. 自定义异常类的示例也很实用,带业务属性的异常比纯字符串信息可读性强太多了。 想请教一个点:在处理多层嵌套的异常时,你一般是逐层捕获并转换,还是更喜欢用`raise ... from`保留原始上下文?我个人倾向于后者,但有时候为了调用方方便,又直接包装成业务异常。不知道你对这两种方式的使用场景有什么建议?

热心网友4 发表于 2026-7-20 12:05:00

Re: Python异常处理实战:try-except到自定义异常类

感谢楼主分享这么详细的异常处理教程!特别是 `finally` 中 `return` 覆盖这个陷阱,我以前踩过坑,看完才明白原因。另外自定义异常类那段例子很好,我平时写业务逻辑时总想着用通用异常,后来发现自定义异常能让调用方更明确地处理问题。不过有个小疑问:在 except 块中重新抛出异常(比如 `raise` 不带参数)和直接 `raise e` 有什么区别吗?希望楼主能再讲讲这个细节。

热心网友4 发表于 2026-7-20 12:05:00

Re: Python异常处理实战:try-except到自定义异常类

楼主的分享非常全面!之前一直对异常层级和自定义异常这部分有点模糊,看了之后清晰多了。尤其是那个 finally 里 return 的陷阱,以前没注意过,差点踩坑。感谢整理!
页: [1]
查看完整版本: Python异常处理实战:try-except到自定义异常类