Print也许是Python中使用频率最高的一个函数。很多小白都是从Hello World程序开始认识Python,而Python的Hello World程序只有一行,那就是调用内置的Print函数,向控制台输出字符串“Hello World”。
不仅小白,哪怕是Python开发者,通常也是Print函数的重度用户。Print最大的应用场景,便是用于调试Python程序。比如,用Print打印程序的运行步骤,或用Print打印程序运行过程中某个变量的值的变化情况。由于Python是无须编译就能执行的解释型编程语言,因此加上Print调试语句后,开发者能够立即执行Python程序,得到调试结果。
使用Print进行调试的好处是非常直白,易于上手,但是弊端也是明显的:如果要打印的信息比较多,就需要写很多行的Print语句;在调试结束后,往往还需要逐一删除这些语句。这是一个繁琐的过程。一言以蔽之,使用Print调试的缺点是效率较低。
除了Print函数,另一种常用的Python程序调试工具是Python的日志模块(logging)。Logging模块根据预定义的格式,将需要的信息写入日志文件。通过跟踪日志文件,也可以实现调试的目的。
使用logging进行调试,看起来比Print函数更规范一些。并且,当调试结束后,logging语句无须删除,在产品环境中依然可以发挥作用。另外,logging模块还支持我们通过调整log等级(INFO/DEBUG/WARN/ERR)来动态决定打印何种日志。美中不足的是,logging模块在使用之前需要进行比较繁琐的配置(Setup),因此使用难度稍大,不够方便。
那么,有没有更好的调试办法呢?最近,Github上出现了一个新的专门用于调试Python程序的第三方库,名叫PySnooper。Snooper在英文中是监听器的意思。PySnooper,顾名思义,就是监听Python程序执行过程的工具。PySnooper一经问世,便引起Python社区的严重关注。仅仅一个月时间便收获了10K+个STAR,着实十分火爆。
相比Print调试往往需要写很多行Print语句,使用PySnooper仅仅一行代码就能实现对整个函数的调试,更加高效;相比Logging模块,使用PySnooper无需进行繁琐的配置,更加简单。
直接看下面这个例子吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)
执行这段代码,输出结果(节选)如下:
1
2
3
4
5
6
7
8
9
10
11
Starting var:.. number = 6
15:29:11.327032 call 4 def number_to_bits(number):
15:29:11.327032 line 5 if number:
15:29:11.327032 line 6 bits = []
New var:....... bits = []
15:29:11.327032 line 7 while number:
15:29:11.327032 line 8 number, remainder = divmod(number, 2)
New var:....... remainder = 0
Modified var:.. number = 3
15:29:11.327032 line 9 bits.insert(0, remainder)
Modified var:.. bits = [0]
可见,只需要导入PySnooper模块,并且给函数加上装饰器@pysnooper.snoop(),我们就可以实现对一个Python函数的监听(调试)。
在上面这个例子中,根据输出结果,我们可以得到:
-
程序执行步骤的顺序,比如执行结果的第2行告诉我们,在15:29:11.327032这一时刻执行了def number_to_bits这一行代码。
-
程序中变量的值的变化情况,比如执行结果的第9行告诉我们,局部变量number此时的值发生了变化,变成了3。
PySnooper支持灵活多样的程序调试,包括但不限于:
-
给函数添加装饰器@pysnooper.snoop(),完成对函数的监听。
-
使用with pysnooper.snoop()语句,实现对程序块(block),即一行或者多行程序进行监听。
-
使用@pysnooper.snoop(‘/my/log/file.log’),将监听结果重定向到文件系统。
- 监听非局部变量的值:
1
@pysnooper.snoop(variables=('foo.bar','self.whatever'))
- 监听一个列表或者字典变量的所有元素或者属性:
1
@pysnooper.snoop(watch=('foo.bar','self.x["whatever"]'))
- 深度监听——监听函数中的行所调用的其他函数:
1
@pysnooper.snoop(depth=2)
- 在多线程程序中,指定监听哪些线程:
1
@pysnooper.snoop(thread_info=True)
PySnooper的更多高级用法参见:https://github.com/cool-RR/PySnooper#advanced-usage。
另外,PySnooper的安装十分简单:
1
pip install pysnooper
总结一下,PySnooper是一个使用简单,功能强大,效率高的Python调试工具,聚集各种优点于一身,难怪这么快就受到了社区的热烈欢迎。
我是肖哥shelwin,一个高质量软件工程实践者和推动者。欢迎扫描下方二维码,添加我的个人公众号测试不将就,获得更多自动化测试, 持续集成, 软件工程实践, Python编程等领域原创文章。