正确捕获所有常规异常应使用 except exception:,而非 except: 或 except baseexception:,因前者会吞掉 keyboardinterrupt、systemexit 等系统级信号,破坏程序中断语义与退出机制。

捕获所有异常的正确写法是 except Exception:,不是 except:
直接写 except: 看似“捕获一切”,但它会吞掉 KeyboardInterrupt、SystemExit 这类系统级信号,导致 Ctrl+C 无法中断程序、os._exit() 失效。这是最常被忽略的安全隐患。
真正需要“兜底”的场景(比如顶层服务入口、脚本主流程),应该用:
try:
main()
except Exception as e:
log_error(e)
# 不要裸 raise,也不建议 silent ignore
-
Exception是所有常规异常的基类,覆盖ValueError、KeyError、ConnectionError等绝大多数业务异常 - 它不包含
BaseException的子类如SystemExit、KeyboardInterrupt、GeneratorExit—— 这是设计上的保护机制 - 永远不要在
except Exception:块里只写pass或空逻辑,至少记录日志
为什么不能用 except BaseException:
虽然 BaseException 理论上能捕获一切(包括 KeyboardInterrupt),但实际中几乎没正当理由这么做。一旦捕获了 KeyboardInterrupt,用户就无法用 Ctrl+C 安全退出;捕获 SystemExit 会让 sys.exit() 失效,破坏模块协作契约。
常见误用场景:
- 写守护进程时想“防止崩溃”,结果把
KeyboardInterrupt吞了,只能 kill -9 强杀 - 单元测试里用
except BaseException:捕获测试中断,掩盖真实问题 - 误以为“更全=更安全”,实则破坏 Python 的中断语义
真正的未知异常,往往来自第三方库或运行时环境
你写的代码可能抛出明确异常,但调用的 C 扩展、网络库、数据库驱动可能抛出未文档化的子类,甚至同一库不同版本异常类型不同(比如某些 HTTP 库把连接超时从 TimeoutError 改成自定义 RequestTimeout)。
应对策略:
- 优先按具体异常类型分层处理:
except requests.Timeout:、except psycopg2.OperationalError: - 兜底用
except Exception as e:,并打印type(e).__name__和str(e),便于后续归类 - 避免依赖
repr(e),有些异常的__repr__会触发副作用(如重连、日志刷盘) - 生产环境建议加
traceback.format_exc(),但别直接打到 stdout,走统一日志通道
异步代码中 except Exception: 依然有效,但要注意协程生命周期
在 async def 函数里,except Exception: 能捕获 await 表达式抛出的异常,和同步代码行为一致。但容易踩的坑是:异常发生在任务(asyncio.create_task())内部时,若没显式 await 或检查 task.exception(),异常会被静默丢弃。
正确做法:
- 对 fire-and-forget 任务,至少加个简单错误回调:
task.add_done_callback(lambda t: t.exception() and log_error(t.exception())) - 在
async with或async for块外层包try/except,因为底层__aenter__/__anext__可能抛异常 - 不要在
except块里直接await—— 若该 await 再抛异常,且外层没再捕获,就会变成未处理异常
异常类型不是越多越好,关键是让错误暴露得及时、可追溯。真正的“未知”,往往藏在日志格式、上下文丢失、或异步任务无人等待这些地方。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











