KeyboardInterrupt、SystemExit、 GeneratorExit
1、python 内置的异常 层次结构
-
四大系统级异常 (直接继承BaseException)
python异常类型 触发场景 代码设计建议捕获 原因 Exception 所有常规异常的父类 其子类 ✅ 应该 常规程序错误,应该处理 KeyboardInterrupt 用户按 Ctrl+C ⚠️ 谨慎 仅用于清理资源,不要完全忽略 SystemExit sys.exit() 调用 ❌ 不建议 让程序正常退出,除非有特殊需求 GeneratorExit 生成器关闭 ❌ 不需要 生成器内部通常不需要处理
2、KeyboardInterrupt 异常捕获
- 捕获后可以执行清理操作,而不是立即终止程序。
python
try:
print("按Ctrl+C中断程序...")
while True:
pass # 无限循环
except KeyboardInterrupt:
print("\n捕获到键盘中断!")
print("可以优雅地清理资源...")
# 用户按Ctrl+C后输出:
# 按Ctrl+C中断程序...
# 捕获到键盘中断!
# 可以优雅地清理资源...
3、SystemExit 异常捕获
- 捕获后程序不会退出,除非再次raise或调用sys.exit()
python
import sys
try:
print("程序即将退出...")
sys.exit(42) # 退出代码42
except SystemExit as e:
print(f"捕获到SystemExit,退出代码: {e.code}")
# 可以选择阻止退出
print("程序继续运行!")
# 输出:
# 程序即将退出...
# 捕获到SystemExit,退出代码: 42
# 程序继续运行!
4、GeneratorExit 异常捕获
- 当生成器被close()或垃圾回收时,会在生成器内部抛出GeneratorExit。
python
def my_generator():
try:
print("生成器开始运行")
for i in range(10):
yield i
except GeneratorExit:
print("生成器被关闭!")
raise # 重新抛出,或者选择不抛出
gen = my_generator()
print(next(gen)) # 0
print(next(gen)) # 1
gen.close() # 关闭生成器
# 输出:
# 生成器开始运行
# 0
# 1
# 生成器被关闭!
5、实际捕获行为对比,一个完整例子展示所有四种:
python
import sys
import time
def demo_system_exit():
try:
print("\n=== 测试 SystemExit ===")
sys.exit(100)
except SystemExit as e:
print(f"✓ 捕获SystemExit,退出代码: {e.code}")
print(" 程序没有退出,继续执行...")
def demo_keyboard_interrupt():
try:
print("\n=== 测试 KeyboardInterrupt ===")
print(" 按Ctrl+C中断...")
while True:
time.sleep(0.1)
except KeyboardInterrupt:
print("\n✓ 捕获KeyboardInterrupt")
print(" 程序优雅退出...")
def demo_generator_exit():
print("\n=== 测试 GeneratorExit ===")
def generator():
try:
for i in range(5):
yield i
except GeneratorExit:
print(" ✓ 捕获GeneratorExit")
raise # 重新抛出
gen = generator()
print(f" 生成值: {next(gen)}")
print(f" 生成值: {next(gen)}")
gen.close() # 触发GeneratorExit
def demo_exception():
try:
print("\n=== 测试 Exception ===")
raise ValueError("测试错误")
except Exception as e:
print(f"✓ 捕获Exception子类: {type(e).__name__}")
print(f" 消息: {e}")
# 执行所有测试
demo_system_exit()
demo_generator_exit()
demo_exception()
# demo_keyboard_interrupt() # 需要手动按Ctrl+C测试
-
好的实践
python# 场景1:处理常规异常 try: data = risky_operation() except ValueError as e: print(f"参数错误: {e}") except (IndexError, KeyError) as e: print(f"访问错误: {e}") except Exception as e: print(f"未知错误: {e}") logger.exception("发生异常") # 场景2:捕获KeyboardInterrupt进行清理 try: main_loop() except KeyboardInterrupt: print("\n收到中断信号...") cleanup_resources() print("资源清理完成,退出。") sys.exit(0) # 明确退出 -
❌ 不好的实践
python# 过度捕获KeyboardInterrupt try: everything() except KeyboardInterrupt: print("用户想退出,但我不让他退出!") # 继续运行... 这会让用户困惑 # 捕获SystemExit阻止程序退出 try: sys.exit(1) except SystemExit: print("程序想退出,但我不允许!") # 继续运行... 这会破坏退出逻辑