python常用的两种debug方式
-
断点+单步调试
对于较大型项目来说,其流程大致为:先在关键的代码位置加上print语句,通过分析print的值将范围缩小,这个过程可能需要重复多次,使用print的方法,一般可以将范围缩小到一个比较完整的功能模块中;然后在可能出现bug的模块中的关键部分打上断点,进入到断点后使用单步调试,查看各变量的值是否正确,最后根据错误的变量值定位到具体的代码行,最后进行修改。 -
pdb调试
pdb是python自带的一个包(看着像gdb,当然用法也想),为 python 程序提供了一种交互的源代码调试功能,主要特性包括设置断点、单步调试、进入函数调试、查看当前代码、查看栈片段、动态改变变量的值等。
传统的debug的方法的缺点包括:
- 需要在代码中添加print语句,这就改变了原有的代码;
- 在断点调试和单步调试过程中,需要保持持续的专注,一旦跳过了关键点就要从头开始。
解决方案-pySnooper
该工具使用采用装饰器的形式,将函数的运行过程以日志的形式打印到文件中,其记录了运行了哪些代码行,运行的时间及运行到当前代码时各变量的值。根据变量的变化就可以定位问题了。亲自试用该工具后,其优点可总结为以下几点:
- 无需为了查看变量的值,使用print打印变量的值,从而修改了原有的代码;
- 接口的运行过程以日志的形式保存,方便随时查看;
- 可以根据需要,设置函数调用的函数的层数,方便将注意力集中在需要重点关注的代码段;
- 多个函数的日志,可以设置日志前缀表示进行标识,方便查看时过滤;
简单入门
工具安装
pip install pysnooper
官方demo
import pysnooper
@pysnooper.snoop()
def number_to_bits(number):
if number:
bits = []
while number:
number, remainder = divmod(number, 2)
bits.insert(0, remainder)
return bits
else:
return [0]
number_to_bits(6)
输出:
注意在自己测试时文件命名的问题,如果命名为“pysnooper.py”会报错“AttributeError: partially initialized module ‘pysnooper’ has no attribute ‘snoop’ (most likely due to a circular import)”
参数设置
以装饰器的形式使用该工具,其包含了四个参数,参数包括output, variables, depth, prefix。
-
output 参数使用(字符串类型的文件路径)
若使用默认参数,则将中间结果输出到控制台,若填写该参数,则将中间结果写入到该参数指定的目录下,如运行以下代码,其中间结果会保存在装饰器snoop中设置日志保存的路径中,注意这里不会自动创建目录,所以需要事先创建目录,如测试代码中填写路径后需要创建log目录。 -
variables参数使用(vector类型)
在默认参数的情况下,使用该工具只能查看局变量的变化过程,当需要查看局部变量以外变量时,则可以通过variables参数进行设置,比如下方代码,在Foo类型,需要查看类实例的变量self.num1, self.num2, self.sum_value,则可以看将该变量设置当参数传入snoop的装饰器中。 -
depth参数使用(整形,默认值为1)
该参数用来指定记录函数调用层数的结果,默认值为1,若要查看多层函数调用的中间结果,则可将该参数设置为>=2。 -
prefix参数使用(str类型,指定该函数接口的中间结果前缀,默认为空)
不足之处(现在使用版本为1.1.1)
- 无法很好的支持递归调用;
- 调用每个函数的中间结果只能保存在一个文件中,如果需要区分不同文件的结果,需要使用prefix来进行前缀标识;
- 对于跨文件函数调用,不支持记录调用函数所在的文件名;