>>>PyPathPython 学习站
首页›核心篇›kp-015
核心篇 · Core lv.2 核心 kp-015

异常处理

前置知识:kp-007

1. 一句话定义

异常是运行期错误的对象化:try/except 捕获并处理,raise 抛出,finally 兜底清理;Python 文化崇尚 EAFP(先做,出错再说),异常是正常控制流的一部分而非羞耻。

2. 为什么重要

错误处理质量区分"能跑"与"可靠"。Python 没有受检异常,正确粒度的 try/except 与自定义异常层次是 API 设计的核心部分。

3. 前置知识

kp-007(函数)、kp-010(dict 访问的错误路径)。

4. 核心概念

python
try:
    risky()
except ValueError as e:        # 捕获特定异常并绑定
    log(e)
except (KeyError, TypeError):   # 多类型
    pass
else:
    print("try 内没抛错才执行")   # 放"成功路径"代码,缩小 try 范围
finally:
    print("无论如何都执行")        # 清理;资源清理优先用 with(kp-025)

  • 异常层次:BaseException → Exception → 各种具体异常;捕获要写到具体类型,裸 except: 会吞 KeyboardInterrupt/SystemExit(应至少写 except Exception)。
  • raise:raise XxxError("msg") from original_err(异常链,保留因果)。

5. 原理与机制

异常即对象:try/except 依据异常类的继承关系匹配——except ValueError 能捕获 UnicodeDecodeError(其子类)。利用这一层设计 API:

python
class ConfigError(Exception): pass
class MissingKey(ConfigError): pass
class BadValue(ConfigError): pass
# 调用方可以精细捕获 MissingKey,也可以整体捕获 ConfigError

EAFP vs LBYL:

python
# LBYL(先看再拿):两次查找 + 竞态窗口
if "k" in d:
    v = d["k"]
# EAFP(Pythonic):一次查找,失败路径交给异常
try:
    v = d["k"]
except KeyError:
    v = default

6. 关键事实(模型/图示)

text
raise ──► 沿调用栈向上传播 ──► 最近匹配的 except ──► finally 保证执行
      └── 无人捕获 ──► 解释器打印 traceback,进程退出

7. 直观类比

异常像电梯报警:不出事时完全不付出代价(对比"每层都探头检查"的 LBYL);出事时直接冲到最近的能处理这层楼的保安(except)手里,而 finally 是"无论有没有报警,出门都要刷卡"。

8. 实例与案例

python
# 解析外部数据:转换与业务校验分层
def parse_int(s):
    try:
        return int(s)
    except (TypeError, ValueError):
        raise BadValue(f"不是整数: {s!r}") from None

from None 抑制原始 traceback,让报错信息干净聚焦。

9. 常见误区

  1. 裸 except: 或 except Exception: pass —— 吞掉一切错误让 bug 隐身;要捕获就处理、要处理就具体。
  2. try 范围过大 —— try 只包"可能抛错的那一行",成功逻辑放 else,否则不知道错来自哪。
  3. 用异常做常规分支(如循环里抛异常控制结束)—— EAFP 不等于滥用;每秒成千上万次的热路径用返回哨兵或 dict.get。
  4. 忘记异常链 —— 在 except 里再 raise 时用 from e,否则原始现场丢失。

10. 自测题

  1. except: 与 except Exception: 的区别?为什么前者危险?
  2. try/else 中 else 什么时候执行?为什么把代码放 try 外面更好?
  3. raise A() from B 的作用?
参考答案
  1. except: 匹配包括 SystemExit/KeyboardInterrupt 在内的所有异常(BaseException 级),会吞掉用户 Ctrl+C 与退出信号;except Exception: 不含系统级异常。
  2. try 体未抛任何异常时;缩小 try 范围,让 traceback 更准确、意图更清晰。
  3. 显式建立异常因果链:新异常的 __cause__ 指向 B,traceback 显示 "The above exception was..."。

11. 与其他知识点的关系

  • kp-025 上下文管理器:资源清理的专用设施(with)。
  • kp-035 测试:pytest.raises 验证异常行为。
  • kp-036 打包:库的异常层次即公开 API。

12. 延伸阅读

  • Python 官方教程 §8 Errors and Exceptions
  • 《Effective Python》Item:用异常表达预期错误