裸 except: 危险因捕获所有异常(含 KeyboardInterrupt、SystemExit),导致调试困难、资源泄漏;应显式指定异常类型,用 except Exception: 替代,捕获后须重抛或完整记录日志。
为什么 except: 是危险的默认选择
它会捕获所有异常,包括 KeyboardInterrupt(Ctrl+C)、SystemExit(sys.exit() 触发)甚至内存耗尽时的 MemoryError。这些本该让程序中止或交由上层处理的信号,被无声吞掉后,会导致调试困难、资源泄漏、逻辑卡死。
常见现象:脚本在终端按 Ctrl+C 没反应;单元测试因 SystemExit 未抛出而误判通过;服务进程在 OOM 后继续“假运行”。
- 永远显式列出需要处理的异常类型,例如
except ValueError:或except (IOError, OSError): - 若真需兜底,用
except Exception:—— 它排除了BaseException的子类如SystemExit和KeyboardInterrupt - 绝不在生产代码里写裸
except:,CI 流水线应配置pylint规则bare-except报警
except Exception as e: 后不 re-raise 的静默失败
捕获后只打印日志却不重新抛出,会让上游调用方误以为操作成功。尤其在函数返回值无明确错误标识(如只返回 None 或布尔值)时,问题会逐层掩盖。
典型场景:数据库连接函数捕获 ConnectionError 后仅写日志并返回 False,但调用方未检查返回值,继续执行后续 SQL,最终报错位置远离真实原因。
立即学习“Python免费学习笔记(深入)”;
- 捕获后若无法本地恢复,应至少保留原始 traceback:
raise(原样重抛)或raise e from None(清除旧上下文) - 若必须吞异常(如清理临时文件),务必记录完整异常信息:
logging.exception("cleanup failed") - 避免用
str(e)拼接日志——它丢弃类型、traceback 和 cause 链
宽泛捕获掩盖了真正的编程错误
比如把 KeyError 和 TypeError 一起写进 except (KeyError, TypeError):,表面看是“兼容性处理”,实则混淆了两类根本不同的问题:一个是数据缺失(应检查输入或加默认值),一个是类型误用(应修正逻辑或加类型断言)。