Python-调试工具:pysnooper模块
0 前言
1 pysnooper模块(第三方库)
Pysnooper 是一款大受欢迎的Debug模块,pysnooper模块比print更方便,以装饰器的形式存在,安装方式为:
pip install pysnooper
使用方式为:
import pysnooper
@pysnooper.snoop(output=, watch=, prefix=, custom_repr=, depth=)
def example():
1.1 调试结果输出
被@pysnooper.snoop()
修饰的函数运行过程中所有函数内部局部变量的变化过程都会被记录下来
@pysnooper.snoop()
不加任何参数时,默认输出到标准输出
import pysnooper
@pysnooper.snoop()
def example():
a = 5
b = 'x'
a = a + 1
b = b + 'y'
example()
结果输出:
Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
10:43:00.189184 call 4 def example():
10:43:00.189184 line 5 a = 5
New var:....... a = 5
10:43:00.189184 line 6 b = 'x'
New var:....... b = 'x'
10:43:00.189184 line 7 a = a + 1
Modified var:.. a = 6
10:43:00.189184 line 8 b = b + 'y'
Modified var:.. b = 'xy'
10:43:00.189184 return 8 b = b + 'y'
Return value:.. None
Elapsed time: 00:00:00.000000
@pysnooper.snoop()
添加output
参数时,输出到log文件
import pysnooper
@pysnooper.snoop(output='out.log')
def example():
a = 5
b = 'x'
a = a + 1
b = b + 'y'
example()
1.2 设置调试日志的前缀
当使用@pysnooper.snoop()
跟踪多个函数时,调试日志会显得杂乱无章,不方便查看。在这种情况下,可以使用prefix
参数,方便为不同的函数设置不同的标志
import pysnooper
@pysnooper.snoop(prefix='example_1:')
def example_1():
a = 5
a = a + 1
@pysnooper.snoop(prefix='example_2:')
def example_2():
b = 'x'
b = b + 'y'
example_1()
example_2()
结果输出:
example_1:Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
example_1:10:50:06.741409 call 4 def example_1():
example_1:10:50:06.741409 line 5 a = 5
example_1:New var:....... a = 5
example_1:10:50:06.741409 line 6 a = a + 1
example_1:Modified var:.. a = 6
example_1:10:50:06.741409 return 6 a = a + 1
example_1:Return value:.. None
example_1:Elapsed time: 00:00:00.000000
example_2:Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
example_2:10:50:06.741409 call 9 def example_2():
example_2:10:50:06.741409 line 10 b = 'x'
example_2:New var:....... b = 'x'
example_2:10:50:06.741409 line 11 b = b + 'y'
example_2:Modified var:.. b = 'xy'
example_2:10:50:06.741409 return 11 b = b + 'y'
example_2:Return value:.. None
example_2:Elapsed time: 00:00:00.000000
1.3 自定义对象的格式输出
python当中有不少数据类型是不支持print
输出的,在这种情况下,可以使用custom_repr
参数,自定义数据类型转化为str
的方式
以正则表达式re模块为例,正常情况如下:
import pysnooper
import re
@pysnooper.snoop()
def example():
pattern = re.compile(r'[a-zA-Z]\w*')
result = re.match(pattern, 'fun%_LIST')
example()
输出结果为:
Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
11:08:27.942424 call 5 def example():
11:08:27.942424 line 6 pattern = re.compile(r'[a-zA-Z]\w*')
New var:....... pattern = re.compile('[a-zA-Z]\\w*')
11:08:27.942424 line 7 result = re.match(pattern, 'fun%_LIST')
New var:....... result = <re.Match object; span=(0, 3), match='fun'>
11:08:27.942424 return 7 result = re.match(pattern, 'fun%_LIST')
Return value:.. None
Elapsed time: 00:00:00.000000
可与看到:
函数没有return
时,返回Return value:.. None
pattern
保存的不是数据类型,而是pattern = re.compile(r'[a-zA-Z]\w*')
这个公式,每次调用pattern
参数,都会重新处理一次公式
result
的数据类型是re.Match,需要group()
方法才能转化为字符串形式
使用custom_repr
参数后:
import pysnooper
import re
def print_match_object(obj):
return obj.group()
@pysnooper.snoop(custom_repr=(re.Match, print_match_object))
def example():
pattern = re.compile(r'[a-zA-Z]\w*')
result = re.match(pattern, 'fun%_LIST')
example()
输出结果为:
Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
11:13:54.449604 call 8 def example():
11:13:54.449604 line 9 pattern = re.compile(r'[a-zA-Z]\w*')
New var:....... pattern = re.compile('[a-zA-Z]\\w*')
11:13:54.449604 line 10 result = re.match(pattern, 'fun%_LIST')
New var:....... result = fun
11:13:54.449604 return 10 result = re.match(pattern, 'fun%_LIST')
Return value:.. None
Elapsed time: 00:00:00.000000
custom_repr
输入一个元组(re.Match, print_match_object)
,第一个元素是变量数据类型re.Match,第二个元素是处理将re.Match处理为可输出形式的函数print_match_object
定义函数print_match_object(obj)
,其中obj
为要处理的re.Match数据类型,并return
处理后的可输出数据
1.4 跟踪非局部变量
PySnooper
是以函数为单位进行调试的,它默认只会跟踪函数体内的局部变量,若想跟踪全局变量,可以给 pysnooper.snoop()
加上watch
参数
- 不加
watch
参数时
import pysnooper
class Calculation():
number = 20
@pysnooper.snoop()
def add(self, input):
self.number = input + 10
cal = Calculation()
cal.add(10)
结果只能显示self
是Calculation类
Source path:... D:/6_SoftwareTest/snoop/main.py
Starting var:.. self = <__main__.Calculation object at 0x000001CCCD0E6F10>
Starting var:.. input = 10
14:06:49.884343 call 8 def add(self, input):
14:06:49.884343 line 9 self.number = input + 10
14:06:49.884343 return 9 self.number = input + 10
Return value:.. None
Elapsed time: 00:00:00.000000
- 加
watch
参数时
import pysnooper
class Calculation():
number = 20
@pysnooper.snoop(watch='self.number')
def add(self, input):
self.number = input + 10
cal = Calculation()
cal.add(10)
结果可以显示self.number
的变化:
Source path:... D:/6_SoftwareTest/snoop/main.py
Starting var:.. self = <__main__.Calculation object at 0x0000018ADC732A60>
Starting var:.. input = 10
Starting var:.. self.number = 20
14:11:36.094900 call 8 def add(self, input):
14:11:36.094900 line 9 self.number = input + 10
14:11:36.094900 return 9 self.number = input + 10
Return value:.. None
Elapsed time: 00:00:00.000000
watch
参数可以使用list
或tuple
输入多个变量
@pysnooper.snoop(watch=('out["foo"]', 'foo.bar', 'self.foo["bar"]'))
1.5 设置跟踪函数的深度
当你使用 PySnooper
调试某个函数时,若该函数中还调用了其他函数,PySnooper
是不会傻傻的跟踪进去的。
如果你想继续跟踪该函数中调用的其他函数,可以通过指定 depth
参数来设置跟踪深度(不指定的话默认为 1)
@pysnooper.snoop(depth=2)
def demo_func():