Python: errors
1 · Python: Errors and Exceptions#
Python有两类错误:语法错误(解析期)和异常(运行期)。
1.1 · 异常层级
BaseException # 所有异常的根基类
├── SystemExit # sys.exit()
├── KeyboardInterrupt # Ctrl+C
├── GeneratorExit # generator.close()
└── Exception # 所有"常规"异常的基类(捕获时通常用这个)
├── ValueError
├── TypeError
├── OSError
│ └── FileNotFoundError
├── RuntimeError
│ └── RecursionError
├── StopIteration
├── ExceptionGroup # 3.11+
└── ...
except Exception能捕获几乎所有异常,但不捕获SystemExit/KeyboardInterrupt/GeneratorExit。
1.2 · 捕获异常 try/except#
执行流:
- 执行 try 子句
- 无异常 → 跳过 except → 执行 else → 执行 finally
- 有异常 → 匹配第一个 except → 执行 finally;无匹配则 finally 后 re-raise
except 匹配规则:匹配异常是该类或其子类的实例。 先写子类、后写父类。
try:
x = int(input("number: "))
except ValueError: # 捕获特定异常
print("invalid")
except (TypeError, OSError) as e: # 多个异常 + 绑定实例
print(e)
except Exception as e: # 兜底(捕获后应 re-raise 或记录)
print(f"Unexpected {e=}, {type(e)=}")
raise
else: # try 没有异常时执行(避免意外捕获)
print("success")
finally: # 无论如何都执行(清理资源)
print("cleanup")
finally 的陷阱
finally中的return会吞掉异常和 try 的返回值(3.14 起发SyntaxWarning,PEP 765)finally中的break/continue同理会阻止异常 re-raise
1.3 · 抛出异常 raise#
raise ValueError("bad value") # 抛出异常实例
raise ValueError # 等价于 raise ValueError()
raise # re-raise 当前异常(保留原始traceback)
1.4 · 异常链 Exception Chaining#
# 显式链:raise ... from exc → __cause__
try:
connect()
except ConnectionError as exc:
raise RuntimeError("DB failed") from exc
# traceback: "The above exception was the direct cause of ..."
# 隐式链:except 中 raise 新异常 → __context__(自动附加)
# traceback: "During handling of the above exception, another exception occurred ..."
# 禁用链:from None
raise RuntimeError("clean msg") from None
1.5 · 自定义异常
约定:命名以
Error结尾;继承Exception(不是BaseException)。
class AppError(Exception):
"""应用基础异常"""
class NotFoundError(AppError):
def __init__(self, resource: str, id: str):
self.resource = resource
self.id = id
super().__init__(f"{resource} {id} not found")
1.6 · with 语句(预定义清理)#
优于
try/finally手动关闭;实现了上下文管理器协议 (__enter__/__exit__) 的对象都可用。
with open("file.txt") as f: # __enter__ / __exit__ 协议
data = f.read()
# 无论是否异常,f 都会被关闭
1.7 · ExceptionGroup 与 except* (3.11+)#
并发场景下多个任务可能同时失败,需要同时报告多个异常。
# 创建
raise ExceptionGroup("problems", [OSError("e1"), ValueError("e2")])
# 捕获:except* 按类型从 group 中选择性提取
try:
...
except* OSError as eg: # eg 是只含 OSError 的子 ExceptionGroup
handle_os(eg.exceptions)
except* ValueError as eg:
handle_val(eg.exceptions)
# 未匹配的异常会继续 re-raise
1.8 · add_note (3.11+)#
常见用法:在 ExceptionGroup 中为每个子异常附加上下文信息(如迭代序号)。
try:
raise TypeError("bad type")
except Exception as e:
e.add_note("additional context") # 追加到 __notes__ 列表
raise
# traceback 末尾显示 notes
1.9 · 与 Go 错误处理的对比#
| 维度 | Python | Go |
|---|---|---|
| 机制 | 异常(抛出/捕获,控制流跳转) | 值(返回/检查,显式控制流) |
| 传播 | 自动沿调用栈向上传播 | 手动 return err |
| 多错误 | ExceptionGroup + except* | errors.Join |
| 封装 | raise ... from exc(异常链) | fmt.Errorf("%w", err) |
| 清理 | with/finally | defer |
| 穷举 | 无编译期保证(同Go) | 无编译期保证 |
异常与值的核心区别在于控制流:
- 异常是隐式传播、显式捕获(抛出后自动沿调用栈上跳,不捕获则崩溃)
- 值是显式传播、显式检查(留在当前调用点,不处理则静默吞掉)