人人都会AI编程

11.1 异常体系:异常类继承关系、常见内置异常

更新时间:2026-07-12

程序运行时不可避免会遇到错误。Python 处理错误的机制就是异常——当出错时,解释器“抛出”一个异常对象,如果没人“捕获”,程序就会终止并打印 Traceback。掌握异常体系的构成,是写出健壮、好调试的 Python 代码的基础。

异常类的继承关系

Python 中所有异常都继承自 BaseException,但我们实际关心的异常绝大部分是 Exception 的子类。这个层级关系直接决定了 except 的捕获范围。

重要的继承链大致如下:

BaseException
 ├── SystemExit           # sys.exit() 引发
 ├── KeyboardInterrupt    # 用户按 Ctrl+C
 ├── GeneratorExit        # 生成器关闭时
 └── Exception            # 所有常规错误的基类
      ├── ArithmeticError         # 数值计算错误
      │    ├── ZeroDivisionError  # 除以零
      │    └── OverflowError      # 数值溢出
      ├── AssertionError          # assert 语句失败
      ├── AttributeError          # 属性访问或赋值失败
      ├── ImportError             # 导入模块失败
      │    └── ModuleNotFoundError # 找不到模块
      ├── LookupError             # 索引或键查找失败
      │    ├── IndexError         # 序列索引越界
      │    └── KeyError           # 字典键不存在
      ├── NameError               # 变量名未找到
      │    └── UnboundLocalError  # 使用了未绑定的局部变量
      ├── OSError                 # 系统调用失败
      │    ├── FileNotFoundError  # 文件不存在
      │    ├── PermissionError    # 无权限操作
      │    ├── TimeoutError       # 操作超时
      │    └── IsADirectoryError  # 用文件操作打开目录
      ├── RuntimeError            # 其他运行时错误
      ├── SyntaxError             # 语法错误
      │    └── IndentationError   # 缩进错误
      ├── TypeError               # 类型不匹配的操作
      ├── ValueError              # 类型正确但值不合法
      └── UnicodeError            # Unicode 编解码错误

关键点

  • except Exception: 能捕获大部分预期内的错误,但不会捕获 SystemExitKeyboardInterrupt 等系统级异常,这是一个好的默认习惯。
  • 子类异常会被父类 except 捕获,所以写多个 except 时要把子类放前面,否则父类会先“吃掉”异常,导致子类捕获分支永不生效。

常见内置异常速查表

| 异常类 | 典型触发场景 | 简单示例 |
|--------|-------------|----------|
| AttributeError | 访问对象不存在的属性 | [1,2,3].non_exist |
| ImportError / ModuleNotFoundError | 导入不存在的模块或包 | import nonexistent_module |
| IndexError | 列表/字符串索引越界 | lst = [1,2]; lst[5] |
| KeyError | 字典键不存在 | d = {}; d['missing'] |
| NameError | 使用未定义的变量名 | print(undefined_var) |
| TypeError | 对不兼容类型做操作 | '3' + 5 |
| ValueError | 类型正确但值不合理 | int('abc') |
| ZeroDivisionError | 除数为零 | 1 / 0 |
| FileNotFoundError | 打开不存在的文件 | open('nofile.txt') |
| PermissionError | 无权限操作文件 | open('/etc/shadow', 'w') |
| OSError | 系统级 IO 错误(父类,也直接出现) | 磁盘满、文件系统故障 |

如何诊断和利用异常

  • 查看错误信息:Traceback 最后一行会给出异常类型和描述,如 ValueError: invalid literal for int() with base 10: 'abc',它已经告诉你哪里出了问题。
  • try/except 精准捕获:避免写 except:except Exception: 一把抓。先捕获最具体的异常,最后再用宽泛的兜底。
  • 自定义异常:继承 Exception 或其子类,简单就能创建业务含义明确的异常,提高代码可读性。
class OrderValidationError(ValueError):
    """订单数据校验失败"""
    pass

def validate_order(data):
    if data['amount'] <= 0:
        raise OrderValidationError("订单金额必须大于0")

在实际开发中,熟练掌握异常体系有几个直接好处:

  • 排查问题更快:一看异常类型就大概知道是哪种错误。
  • 代码更健壮:懂得哪些异常可能发生,才能写出合理的捕获和处理逻辑。
  • 团队协作更顺畅:统一的自定义异常让错误传递有统一的“语言”,而不是靠返回码或打印日志四处散落。