裸 except: 会吞掉 KeyboardInterrupt 和 SystemExit,导致无法用 Ctrl+C 中断或 sys.exit() 退出;应改用 except Exception: 并配合日志记录,避免掩盖 NameError 等开发缺陷类异常。

直接写 except: 会吞掉 KeyboardInterrupt 和 SystemExit
按下 Ctrl+C 时,Python 默认抛出 KeyboardInterrupt;调用 sys.exit() 时抛出 SystemExit。这两个异常都继承自 BaseException,而非 Exception。而裸 except: 捕获的是所有异常——包括它们。
后果很直接:你再也无法用 Ctrl+C 中断程序,sys.exit(0) 也不生效,只能强杀进程。这在调试、脚本部署或 CLI 工具中极其危险。
- 反例:
except:写在顶层循环里 →Ctrl+C失效 - 替代方案:用
except Exception:(不捕获BaseException子类) - 更稳妥的做法:只捕获你明确知道如何处理的异常类型
except Exception: 看似安全,实则掩盖 NameError 和 AttributeError
这类异常通常代表代码缺陷:变量名写错、对象没某个属性、函数根本没定义。它们不是“运行时意外”,而是“开发阶段就该暴露的问题”。一旦被 except Exception: 捕获并静默处理,程序会继续执行,但状态早已错乱。
比如你写了 user.get_name(),但实际方法叫 get_full_name(),抛出 AttributeError。如果被兜底捕获,后续逻辑可能基于一个 None 做计算,最终结果错误却无提示。
立即学习“Python免费学习笔记(深入)”;
- 这类错误本该在测试或首次运行时立刻暴露
-
except Exception:让它们变成“幽灵 bug”,排查成本指数级上升 - 真正需要兜底的场景(如顶层日志),必须记录完整堆栈:
logging.exception("unhandled error")
性能损耗小但语义污染大:一次 except: 会让整段逻辑失去可推断性
Python 的异常机制本身开销不大,问题不在性能,而在代码契约的崩塌。当你把一大段逻辑包进 try,又用 except: 收尾,等于告诉阅读者:“这段代码任何地方都可能出任何错,且我选择不说明是哪种”。这破坏了函数职责的清晰边界。
典型症状:
- 函数返回值含义模糊(成功/失败/部分成功全靠猜)
- 无法做静态分析或类型检查(mypy、pyright 会放弃推断)
- 单元测试难以覆盖分支(你甚至不知道哪些路径会进
except)
更隐蔽的风险是:它鼓励开发者跳过前置校验。比如本该用 if key in dict 避免 KeyError,却直接上 try/except KeyError —— 这在高频路径下反而更慢,且掩盖了数据建模问题。
真正需要“兜底”的地方,except Exception: 也必须配合 raise 或 sys.exit()
仅在极少数位置允许宽泛捕获:主程序入口、异步任务调度器、Web 请求顶层中间件。但即便如此,也不能只是 print("出错了") 就完事。
- 必须记录
traceback.format_exc(),否则等于没日志 - 若无法恢复,应重新抛出:
raise(保留原始堆栈)或raise RuntimeError(...) from e(带上下文包装) - 绝对不要在 finally 里 return,也不要让 except 吞掉异常后还返回有效值——这会让调用方误判执行成功
最常被忽略的一点:finally 块的执行优先级高于 sys.exit() 和未捕获异常的终止流程。这意味着,哪怕你在 except 里写了 sys.exit(1),finally 仍会执行——如果里面还有异常,原始错误信息就彻底丢了。


















