Python Python: 告别 Print?

肖哥@测试不将就 · June 10, 2019 · Last by 肖哥@测试不将就 replied at June 13, 2019 · 1585 hits

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无需进行繁琐的配置,更加简单

直接看下面这个例子吧。

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)

执行这段代码,输出结果(节选)如下:

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支持灵活多样的程序调试,包括但不限于:

  1. 给函数添加装饰器@pysnooper.snoop(),完成对函数的监听。

  2. 使用with pysnooper.snoop()语句,实现对程序块(block),即一行或者多行程序进行监听。

  3. 使用@pysnooper.snoop('/my/log/file.log'),将监听结果重定向到文件系统。

  4. 监听非局部变量的值:

    @pysnooper.snoop(variables=('foo.bar','self.whatever'))
  5. 监听一个列表或者字典变量的所有元素或者属性:

    @pysnooper.snoop(watch=('foo.bar','self.x["whatever"]'))
  6. 深度监听——监听函数中的行所调用的其他函数:

    @pysnooper.snoop(depth=2)
  7. 在多线程程序中,指定监听哪些线程:

    @pysnooper.snoop(thread_info=True)

    PySnooper的更多高级用法参见:https://github.com/cool-RR/PySnooper#advanced-usage

另外,PySnooper的安装十分简单:

pip install pysnooper

总结一下,PySnooper是一个使用简单,功能强大,效率高的Python调试工具,聚集各种优点于一身,难怪这么快就受到了社区的热烈欢迎。

共收到 13 条回复 时间 点赞

看起来调试起来很方便

好工具,马上分享给同事

神奇的函数装饰器,mark

感谢分享,试试看~

目前也在用这个库调试,比print 好用很多

6666666,谢谢分享

根据logging自研一个日志类更好,本身导入新的也有开销。 Print调式语言加开关就行了,print效率不低,要低也是后面用了不当的+追加,要用format模式。

这个精准测可以用,平时开发debug效率不一定比肉眼或print好

zailushang 回复

谢谢支持;)

Benjamin 回复

装饰器大法好!

补充一下,其实文章还漏了一种调试手段,那就是设置断点进行单步调试。例如:pdb。

HiKari 回复

有道理

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up