16.7 pdb:交互式调试工具
pdb为Python程序实现了一个交互式调试环境。它包括一些特性,可以暂停程序,查看变量值,以及逐步监视程序执行,从而能了解程序具体做了什么,并查找逻辑中存在的bug。
16.7.1 启动调试工具
16.7.1.1 从命令行运行
使用调试工具最直接的方法是从命令行调试工具,提供程序作为输入,使它知道要运行什么。
#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2019 Iglesias. All rights reserved.
#
class MyObj:
def __init__(self,num_loops):
self.count = num_loops
def go(self):
for i in range(self.count):
print(i)
return
if __name__ == '__main__':
MyObj(5).go()
从命令行运行调试工具时,它会加载源文件,并在找到的第一条语句处停止执行。在这里,它会在第20行评估类MyObj的定义之前停止。
说明:pdb打印一个文件名时会在输出中包含各模块的完整路径。为了简化这一节中的例子,示例输出中的路径被替换为一个省略号(…)。
16.7.1.2 在解释器中运行
很多Python开发人员在开发模块的较早版本时会使用交互式解释器,因为这样他们能反复试验,而不用像创建独立脚本时那样,需要完整地保存/运行/重复周期。要在一个交互式解释器中运行调试工具,可以使用run()或runeval()。
run()的参数是一个串表达式,可以由Python解释器估算。调试工具会进行解析,然后在估算第一个表达式之前暂停执行。这里介绍的调试工具命令可以用来导航和控制执行。
16.7.1.3 在程序中运行
前面的两个例子都是从程序一开始就启动调试工具。对于一个长时间运行的进程,问题可能出现在程序执行比较靠后的时刻,更方便的做法是在程序中使用set_trace()启用调试工具。
#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2019 Iglesias. All rights reserved.
#
import pdb
class MyObj:
def __init__(self,num_loops):
self.count = num_loops
def go(self):
for i in range(self.count):
pdb.set_trace()
print(i)
return
if __name__ == '__main__':
MyObj(5).go()
示例脚本的第22行在执行到该点时触发调试工具,使它在第23行暂停。
set_trace()只是一个Python函数,所以可以在程序中任意位置调用。这样就可以跟据程序中的条件进入调试工具,包括从一个异常处理器进入,或者通过一个控制语句的特定分支进入。
16.7.1.4 失败后运行
在程序终止后调试失败被称为事后剖析调试(post-morterm debugging)。pdb通过pm()和post_mortem()函数支持事后剖析调试。
#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2019 Iglesias. All rights reserved.
#
class MyObj:
def __init__(self,num_loops):
self.count = num_loops
def go(self):
for i in range(self.num_loops):
print(i)
return
在这个例子中,第18行上不正确的属性名触发了一个AttributeError异常,导致执行停止,pm()查找活动traceback,并在调用栈中出现异常的位置启动测试工具。