异常捕获需按继承关系由子类到父类排列,否则子类异常会被父类块提前捕获而失效;具体异常应放在宽泛异常之前,确保精确处理优先、兜底逻辑靠后。

因为异常捕获是按代码顺序逐个匹配的,一旦某个 catch 块能处理当前异常,后续所有块就直接跳过——所以具体的小异常必须放在前面,宽泛的大异常得靠后,否则小异常永远没机会被真正捕获。
异常匹配只看第一个命中项
运行时从上到下检查每个 catch 的类型,只要当前异常是该类型的实例(或其子类),就立刻进入该块执行,不再往后看。比如抛出 FileNotFoundException,它既是 FileNotFoundException 本身,也是 OSError 和 Exception 的实例。如果 except Exception: 写在最前,那后面所有更具体的分支都形同虚设。
继承关系决定谁该排前面
异常本质是类,有明确的父子继承链。例如:
- ValueError ← Exception
- FileNotFoundError ← OSError ← Exception
- 自定义异常若继承自 Exception,也属于它的子类
子类更精确、信息更丰富,理应优先获得处理权;父类只是兜底,负责收尾那些没被专门照顾到的情况。
写错顺序的实际后果
常见错误写法:
- 把 except Exception: 放在所有具体异常之前 → 所有子类异常都被吃掉
- 空 except:(等价于 except BaseException:)写在中间或开头 → 连 KeyboardInterrupt(Ctrl+C)都会被拦截,用户无法中断程序
- Java 中父类 catch 写在子类前 → 编译直接报错“Unreachable catch block”,不给运行机会
推荐的排列结构
按业务粒度由细到粗组织:
- 先业务级具体异常(如 UserNotFoundException、InsufficientBalanceException)
- 再系统级常见异常(如 SQLException、IOException)
- 然后通用运行时异常(如 RuntimeException、Exception)
- 最后是极兜底项(仅限必要场景,如 except: 或 catch (Throwable),且需谨慎)











