不能只用except:,因为它会捕获KeyboardInterrupt和SystemExit,导致Ctrl+C无法中断程序、sys.exit()失效;正确做法是显式捕获Exception并排除系统退出类异常,或用元组捕获具体类型如(ValueError, TypeError),Python 3.11+需用except*处理ExceptionGroup。

捕获异常时为什么不能只用 except:?
直接写 except: 会吞掉 KeyboardInterrupt 和 SystemExit,导致 Ctrl+C 无法中断程序、sys.exit() 失效。Python 3.11 默认启用 ExceptionGroup 支持,但裸 except: 仍会捕获所有基类,包括不该拦截的系统信号。
正确做法是显式捕获 Exception,并排除 SystemExit 和 KeyboardInterrupt:
try:
main()
except Exception as e:
logger.error("未预期异常", exc_info=True)
raise # 保留原始退出语义
- 永远别用
except BaseException:—— 它比except:更危险 - 若需捕获多个异常类型,用元组:
except (ValueError, TypeError): - Python 3.11 中
ExceptionGroup需要单独处理:except* ValueError:(注意星号)
记录异常时 exc_info=True 和 traceback.format_exc() 有什么区别?
logger.error("msg", exc_info=True) 会把当前 sys.exc_info() 绑定的异常完整注入日志处理器,包括原始 traceback、局部变量(如果启用了 logging.basicConfig(..., stacklevel=...) 或使用了支持 locals 的 handler);而 traceback.format_exc() 只返回字符串,丢失结构化信息,也无法被日志过滤器或远程采集工具(如 Sentry、ELK)解析。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 始终优先用
exc_info=True,不要手动拼接字符串 - 若需自定义异常上下文(如添加 request_id),用
logger.exception("msg", extra={"request_id": "abc123"}) - 避免在
except块里调用print(e)或str(e)—— 会丢掉 traceback 和 cause 链
如何让日志同时输出到文件和控制台,且异常堆栈不被截断?
默认的 StreamHandler 使用 logging.Formatter 时,若未指定 fmt 中包含 %(pathname)s、%(lineno)d 等字段,或未设置 datefmt,会导致 traceback 显示不全;更关键的是,某些终端(如 Windows PowerShell)对 ANSI 转义序列敏感,可能提前截断长 traceback。
推荐配置:
import logging
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)-8s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
))
logging.getLogger().addHandler(handler)
- 文件 handler 必须用
FileHandler并设置encoding="utf-8",否则 Windows 下中文 traceback 会乱码 - 避免用
basicConfig(level=logging.INFO)后再手动 addHandler —— 它会禁用后续 handler 添加 - Python 3.11 的
ExceptionGrouptraceback 默认换行更密集,建议在 formatter 中加%(funcName)s提高可读性
异步任务(asyncio)中异常为什么总“消失”?
在 asyncio.create_task() 或 asyncio.gather() 中抛出的异常,若未被 await 或显式检查,会被静默丢弃 —— 这不是 bug,是 asyncio 的设计行为。Python 3.11 的 asyncio.run() 会在主协程结束后自动检测未处理的 task 异常并警告,但仅限顶层。
可靠捕获方式:
- 对单个 task:用
asyncio.create_task(coro).add_done_callback(handle_task_result),并在回调中检查task.exception() - 对多个 task:用
await asyncio.gather(*tasks, return_exceptions=True),然后遍历结果判断是否为异常实例 - 全局兜底:设置
asyncio.get_event_loop().set_exception_handler(),但注意该 handler 不接收exc_info元组,只能拿到loop、context字典
最易忽略的一点:async with 或 async for 块内异常,若未被外层 try/except 包裹,也会因协程退出而丢失 traceback 上下文 —— 必须在协程函数内部处理,不能依赖外层同步 try。


















