这里已经有很多回答展示了如何获取行号,但值得注意的是,如果你想要包含“原始数据”的变量,这样你就可以更细粒度地控制你要显示或者格式化的内容,那么使用
traceback
模块,你可以逐帧遍历堆栈并查看存储在帧摘要对象属性中的内容。有几种简单而优雅的方法可以直接操作帧摘要对象。举个例子,假设你想要获取堆栈中最后一帧的行号(它告诉你哪行代码触发了异常),你可以通过访问相关的帧摘要对象来获取它。
选项1:
import sys
import traceback
try:
except Exception as exc:
exc_type, exc_value, exc_tb = sys.exc_info()
stack_summary = traceback.extract_tb(exc_tb)
end = stack_summary[-1]
Option 2:
import sys
import traceback
try:
except Exception as exc:
tbe = traceback.TracebackException(*sys.exc_info())
end = tbe.stack[-1]
在上述示例中,
end
将是一个框架摘要对象:
>>> type(end)
<class 'traceback.FrameSummary'>
这个对象又来自于一个堆栈摘要对象:
>>> type(stack_summary) # from option 1
<class 'traceback.StackSummary'>
>>> type(tbe.stack) # from option 2
<class 'traceback.StackSummary'>
堆栈摘要对象的行为类似于列表,您可以按照任何您想要的方式迭代其中的所有框架摘要对象,以便跟踪错误。框架摘要对象(例如本例中的
end
)包含了行号和其他定位异常发生位置所需的信息:
>>> print(end.__doc__)
A single frame from a traceback.
- :attr:`filename` The filename for the frame.
- :attr:`lineno` The line within filename for the frame that was
active when the frame was captured.
- :attr:`name` The name of the function or method that was executing
when the frame was captured.
- :attr:`line` The text from the linecache module for the
of code that was running when the frame was captured.
- :attr:`locals` Either None if locals were not supplied, or a dict
mapping the name to the repr() of the variable.
如果捕获异常对象(无论是使用
except Exception as exc:
语法还是从
sys.exc_info()
返回的第二个对象),则您将拥有编写自己高度定制的错误打印/记录函数所需的所有内容。
err_type = type(exc).__name__
err_msg = str(exc)
把所有东西放在一起:
将其整合:
from datetime import datetime
import sys
import traceback
def print_custom_error_message():
exc_type, exc_value, exc_tb = sys.exc_info()
stack_summary = traceback.extract_tb(exc_tb)
end = stack_summary[-1]
err_type = type(exc_value).__name__
err_msg = str(exc_value)
date = datetime.strftime(datetime.now(), "%B %d, %Y at precisely %I:%M %p")
print(f"On {date}, a {err_type} occured in {end.filename} inside {end.name} on line {end.lineno} with the error message: {err_msg}.")
print(f"The following line of code is responsible: {end.line!r}")
print("Please make a note of it.")
def do_something_wrong():
try:
1/0
except Exception as exc:
print_custom_error_message()
if __name__ == "__main__":
do_something_wrong()
让我们运行它!
user@some_machine:~$ python example.py
在2022年8月25日凌晨01:31,example.py文件中do_something_wrong函数的第21行发生了ZeroDivisionError错误,错误信息为:division by zero。
导致错误的代码行是:'1/0'
请记下来。
此时,你可以看到如何将此消息打印到堆栈的任何位置:结尾、开头、中间任何位置,或者遍历并打印每个堆栈帧。
当然,traceback
模块提供的格式化功能已经涵盖了大多数调试用例,但了解如何操作traceback对象以提取所需信息非常有用。