核心要点

  • 掌握 try/except/else/finally 语义与执行顺序

  • 能区分 Exception 层级并避免裸 except

  • 知道 raise、自定义异常、with 与上下文管理器

标准回答

一、基本结构

示例代码可以这样理解:
try:
risky()
except ValueError as e:
handle(e)
except (TypeError, KeyError):
handle_multi()
else:
on_success()
finally:
cleanup() # 必定执行

二、最佳实践

  • 捕获具体异常,避免裸 except: 吞掉 KeyboardInterrupt
  • raise ... from e 保留异常链
  • 自定义异常继承 Exception,不要继承 BaseException(除系统级)
  • 文件/锁/连接用 with open(...) as f: 自动释放

三、工程场景

API 调用重试、数据管道脏数据跳过、训练中断保存 checkpoint

常见误区

⚠️ 常见踩坑

误区一:容易答偏的地方:裸 except:(捕获 BaseException)会连 KeyboardInterrupt、SystemExit 一并吞掉,导致 Ctrl+C 都无法中断;except Exception 虽不捕获这些 BaseException 子类,但仍会静默 NameError(如拼写错误)等所有普通异常,同样让排障困难——应捕获具体类型。另一个坑:在 except 里用 raise NewError() 而非 raise NewError() from e,会丢失原始堆栈/异常链,看不到根因。

追问

追问 1else 和 finally 分别在什么时候执行?

elsetry 未抛异常时执行,常用于「成功路径」的收尾;finally 无论是否异常都会执行(除非进程被 kill),常用于释放资源。returntry 中也会先执行 finally 再真正返回。

追问 2Exception 和 BaseException 有什么区别?

BaseException 是所有异常的根;Exception 是其子类,也是绝大多数业务异常的基类。KeyboardInterruptSystemExitGeneratorExit 直接继承 BaseException 而非 Exception,故 except Exception 不会捕获它们——这正是让 Ctrl+C 能中断程序的原因。自定义异常应继承 Exception。

追问 3如何用上下文管理器处理异常?

with 块会在退出(含异常)时调用对象的 __exit__(exc_type, exc_val, tb),可在此释放资源;__exit__ 返回 True 表示「已处理、吞掉异常」,返回 False/None 则异常继续向上抛。常用 contextlib.suppress(FileNotFoundError) 优雅忽略特定异常,或 @contextmanager 装饰生成器自定义 setup/teardown。

🔗 相似问题

同一考点的不同问法,换着练更稳

延伸学习

按主题分类的相关资源,便于系统复习