异常发生时应通过 sys.exc_info()[2] 获取 traceback,再用 inspect.getinnerframes() 提取最内层帧的 f_locals;直接用 inspect.currentframe() 或 f_back 无法准确定位出错行的局部变量。

异常发生时直接访问 traceback 中的 frame locals
Python 的 inspect 模块本身不捕获异常,但配合 sys.exc_info() 和 traceback 可以拿到异常帧对象,进而用 inspect.getframeinfo() 或直接读取 tb.tb_frame.f_locals 获取局部变量。关键不是“用 inspect 抓异常”,而是“在异常上下文中用 inspect 查帧”。
常见错误是试图在 except 块里直接调用 inspect.currentframe() ——它返回的是当前 except 块的帧,不是出错那一行的帧,locals 也对不上。
- 必须从
sys.exc_info()[2](即 traceback 对象)逐级向上找tb_next,直到最内层的tb_frame - 推荐用
traceback.extract_tb()定位位置,再用traceback.format_exception()+sys.exc_info()组合获取完整上下文 - 注意:Jupyter / IPython 里
sys.excepthook被重写,sys.exc_info()在某些交互式场景可能为空,需加判空
用 inspect.getinnerframes() 快速提取所有异常帧 locals
inspect.getinnerframes() 是最省事的封装,它把 traceback 对象自动展开成帧信息列表,每个元素是 (frame, filename, lineno, function, code_context, index) 元组,其中 frame 就带 f_locals。
它比手动遍历 tb.tb_next 更可靠,尤其处理嵌套调用或生成器异常时。
- 必须传入
sys.exc_info()[2],不能传None,否则抛TypeError - 第二个参数
context控制显示多少行源码,默认 1,设为 0 可跳过源码加载(提速,尤其模块未加载时) -
f_locals是字典,但可能包含大量不可序列化对象(如threading.Lock),打印前建议过滤掉__开头键或用pprint防卡死
import sys, inspect
try:
x = 1
y = 0
z = x / y
except:
tb = sys.exc_info()[2]
frames = inspect.getinnerframes(tb)
# 最内层帧即出错位置
last_frame = frames[-1][0]
print("locals at error:", {k: v for k, v in last_frame.f_locals.items() if not k.startswith('__')})
为什么不能在 except 里用 inspect.currentframe().f_back.f_locals
很多人试过在 except 块里写 inspect.currentframe().f_back.f_locals,结果看到的是上一层函数的 locals,不是异常发生的那一行——因为异常已经跳出原作用域,f_back 指向的是 except 所在函数的帧,不是出错语句所在的帧。
调用 Cutout.Pro 视觉处理 API 进行背景移除、人像抠图和照片增强,支持文件上传与图片 URL 输入。
更糟的是,如果异常发生在表达式求值中(比如 func(a, b) 里 b 是未定义变量),f_locals 甚至可能不包含 b,因为解释器在解析参数时就崩溃了,局部变量表还没完整构建。
-
currentframe()返回的是当前执行点的帧,和异常位置无关 -
f_back是调用栈关系,不是异常传播路径;异常传播靠traceback链,不是帧链 - 只有通过
sys.exc_info()[2]拿到的 traceback 才忠实记录了异常实际发生的帧
生产环境慎用 f_locals 直接暴露敏感数据
f_locals 会完整暴露当前作用域所有变量值,包括密码、token、文件路径、数据库连接等。调试时方便,上线后若被日志自动 dump 出来就是严重泄漏。
不是所有变量都适合打印,也不是所有异常都需要全量 locals。
- 避免无条件
print(frame.f_locals),至少做 key 过滤(如剔除'password','token','conn'等) - 考虑用
reprlib.Repr().repr(obj)截断长对象,防日志刷屏或 OOM - 异步框架(如 asyncio)中,
f_locals可能包含coroutine对象,其__dict__不公开,强行访问会报AttributeError
真正难的从来不是怎么拿到 locals,而是判断哪些该拿、哪些该删、在哪一级帧停住——这得结合业务逻辑,没法靠一个函数自动解决。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










