except exception: 不捕获 systemexit,因为 systemexit 直接继承自 baseexception,与 exception 并列,设计上绕过常规异常处理以确保程序可正常退出;必须显式用 except systemexit: 或 except (systemexit, keyboardinterrupt): 才能捕获。

它能捕获,但默认不捕获——SystemExit 是 BaseException 的子类,而 except Exception: 会跳过它;必须显式写 except SystemExit: 才行。
为什么 except Exception 不抓 SystemExit
SystemExit 和 KeyboardInterrupt、GeneratorExit 一样,直接继承自 BaseException,而非 Exception。Python 设计上就让它们“绕过”常规异常处理,避免被意外吞掉导致程序无法退出。
-
except:(空 except)能捕获SystemExit,但强烈不推荐——它也会吞掉KeyboardInterrupt,让你 Ctrl+C 失效 -
except Exception:完全忽略SystemExit,这是最常踩的坑:你以为写了兜底,其实什么都没兜住 - 正确写法只有两种:
except SystemExit:或except BaseException:(后者更宽泛,慎用)
pytest.raises(SystemExit) 为什么经常失效
不是 SystemExit 捕获不了,而是测试执行路径没对上。
- 被测函数里用了
try/except SystemExit却没raise出来,pytest.raises就收不到异常 - 调用的是子进程(比如
subprocess.run(...)),SystemExit发生在子进程里,父进程根本看不见 - 参数传错导致函数根本没走到
sys.exit()那行——比如@pytest.mark.parametrize里某个 case 触发了TypeError,SystemExit压根没机会抛 - Python 3.10+ 中
SystemExit("msg").code是字符串,SystemExit(1).code是整数,断言时类型不匹配会失败
多线程里 try-except 根本看不到 SystemExit
sys.exit() 在子线程里调用,不会终止整个程序,也不会传播到主线程的 try 块中。
-
threading.Thread内部抛SystemExit,只结束该线程,主线程继续运行,外层try完全无感 -
multiprocessing.Process同理——进程隔离,异常不跨边界 - 若真需要响应退出信号,得靠
queue、threading.Event或显式检查thread.is_alive()等协作机制,别指望异常传递
什么时候该捕获 SystemExit,什么时候不该
捕获本身没问题,但动机必须清晰——多数情况下,你不该拦它。
- Web 请求处理中想做清理?可以:
try: ... except SystemExit: cleanup(); raise - 命令行工具想统一日志输出?合理:
except SystemExit as e: log_exit(e.code); raise - 只是想“防止程序退出”?别这么做——这违背
sys.exit()的设计语义,后续逻辑可能出错 - 用
finally做资源清理比捕获SystemExit更可靠,因为finally在任何退出路径下都会执行
真正容易被忽略的点是:你写的 except 看似覆盖了所有异常,其实悄悄放过了 SystemExit 和 KeyboardInterrupt;而测试里看似调用了 sys.exit(),实际执行路径早被其他异常截断了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











