Python Python: 告别 Print?

肖哥shelwin · 2019年06月10日 · 最后由 肖哥shelwin 回复于 2019年06月13日 · 1750 次阅读

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 条回复 时间 点赞
HiKari 回复

有道理

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

Benjamin 回复

装饰器大法好!

在路上 回复

谢谢支持;)

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

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

6666666,谢谢分享

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

感谢分享,试试看~

神奇的函数装饰器,mark

好工具,马上分享给同事

看起来调试起来很方便

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册