异常捕获应按业务边界划定范围,以完整业务步骤为最小可恢复单元;底层抛具体异常,上层依类型响应;慎用裸except,优先明确异常类型;异常后须做状态清理或补偿。

异常捕获的粒度,不是越细越好,也不是越粗越省事,关键看哪里真正需要干预、哪里该让错误自然暴露。
按业务边界划定 try 块范围
把一个函数或一个完整业务步骤(比如“下单→扣库存→发消息”)作为最小可恢复单元,而不是为每一行可能出错的代码都套 try。否则会掩盖真实问题,也增加维护成本。
- ✅ 推荐:对整个支付流程统一捕获 PaymentError,记录日志并回滚事务
- ❌ 避免:在调用第三方 SDK 的每一行都单独 try,再分别 print(e),结果错误被吞掉,后续逻辑却继续执行
分层捕获:底层抛具体异常,上层做策略响应
底层模块(如数据库访问)应主动 raise 自定义异常(如 UserNotFoundError、InsufficientBalanceError),而不是返回 None 或 -1;上层根据异常类型决定是重试、提示用户、还是降级处理。
- 数据库层抛出 UserNotFoundError,表示查无此人
- API 层捕获它,返回 HTTP 404 和友好提示:“用户不存在,请检查账号”
- 而同样一个 ConnectionError,则由框架层统一重试或切换备用服务
慎用裸 except: 和 except Exception:
它们会拦截 KeyboardInterrupt、SystemExit 等系统级信号,导致程序无法被 Ctrl+C 中断,或无法正常退出。真正需要兜底时,优先用 except (ValueError, TypeError, OSError):,或明确补上 except BaseException:(仅限极少数日志/清理场景)。
立即学习“Python免费学习笔记(深入)”;
- ❌ except: —— 隐式等价于 except BaseException:,风险极高
- ⚠️ except Exception: —— 拦住大部分错误,但仍放行系统退出类异常
- ✅ except (ConnectionError, TimeoutError): —— 明确、安全、意图清晰
异常后务必做“有状态”的清理或补偿
捕获异常不等于问题结束。要判断当前操作是否已产生副作用:文件是否已写入部分数据?数据库是否已插入中间记录?消息是否已发出但未确认?
- 用 with 语句自动管理资源(文件、连接、锁)
- 涉及多步更新时,优先设计成幂等操作,或引入补偿事务(如发送失败则写入延迟队列)
- 不要只写 logging.exception() 就完事,要回答:“现在系统处于什么状态?下一步该谁来处理?”










