PySnooper介绍
print对很多人来说算是最常用的debug神器了,只需要在合适的地方插入print打印变量的值,就能判断代码在这个地方是不是还按照你的预期在运行。不过这也带来一些麻烦:首先需要找准位置,然后写出对应的print语句。当函数复杂、变量多的时候确实挺烦的,通常需要多个地方插入多个变量的print。
最近一个刚刚上线的Python包“PySnooper”更优雅地解决了这一需求——只需要对关注的函数前加上一个装饰器@pysnooper.snoop(),就可以将这个函数运行的时间,行号、运行过程中变量的数值以及代码等内容输出到stderr。项目已经有超过13.1K个star,可以说是相当火爆了。
PySnooper有以下两个作用:
首先,学习与理解Python执行逻辑,在新手学习中,经过困扰的是一段代码不知道背后机器是如何进行执行的,PySnooper可以解决这个问题。
其次,程序debug调试,这个就是开头的作用,用于程序调试。
使用方法
安装很简单,直接使用pip就行:
1 | $ pip install pysnooper |
使用也依然简单,我们这里写个简单的运算斐波那契数列的函数进行测试:
1 | from __future__ import print_function |
可以看到PySnooper输出了函数每条语句的运行时间(方便做性能优化测试)、变量的初始化赋值和修改后的值、行号、代码都进行了输出。最后输出的则是程序运行的结果。
或者,如果您不想追踪整个函数,则可以将相关部分包装在一个with块中:
1 | 导入 pysnooper |
输出类似:
1 | New var:....... i = 9 |
相关参数
将输出重定向到文件,PySnooper会输出到stderr。如果想要输出到文件,可以在命令行使用2> /my/log/file.log重定向到文件。也可以给装饰器增加一个参数:
1 | @pysnooper.snoop('/my/log/file.log') |
如果想检查一些非局部变量,也可以使用参数variables:
1 | @pysnooper.snoop(variables=('foo.bar', 'self.whatever')) |
通过参数depth设定调用函数的深度:
1 | @pysnooper.snoop(depth=2) |
如果stderr和stdout一起输出可能会看不太清楚,可以通过prefix参数给PySnooper的输出结果添加一个前缀,比如:
1 | @pysnooper.snoop(prefix='ZZZ ') |
高级用法
使用watch_explode扩展值,以查看他们的所有列表/字典的属性或项目:
@ pysnooper.snoop(watch_explode =(' foo ',' self '))
watch_explode会自动猜测如何根据其类扩展传递给它的表达式。通过使用以下类别之一,您可以更加具体:
1 | import pysnooper |
使用exclude参数排除特定的键/属性/索引,例如Attrs(‘x’, exclude=(’_foo’, ‘_bar’))。
在其后添加一个切片,Indices以仅查看该切片中的值,例如Indices(‘z’)[-3:]。
$ export PYSNOOPER_DISABLED = 1 #这使得PySnooper不做任何监听
这将输出如下行:
1 | Modified var:.. foo[2] = 'whatever' |
以前缀开头所有侦听线,以便为它们轻松grep:
@ pysnooper.snoop(前缀= ' ZZZ ')
删除所有与机器相关的数据(路径,时间戳,内存地址),以便与其他跟踪轻松进行比较:
@ pysnooper.snoop(normalize = True)
在多线程应用程序上,确定在输出中监听到哪个线程:
@ pysnooper.snoop(thread_info = True)
PySnooper支持装饰生成器。
如果您使用来修饰一个类snoop,它将自动将修饰符应用于所有方法。(不包括属性和其他特殊情况。)
您还可以自定义对象的代表:
1 | def large(l): |
您将获得l = list(size=10000)该列表和a = ndarray(shape=(10, 10), dtype=float64)ndarray。的custom_repr顺序匹配,如果一个条件匹配,将不检查其他条件。
默认情况下,变量和异常被截断为100个字符。您可以自定义:
@ pysnooper.snoop(max_variable_length = 200)
您也可以使用max_variable_length=None它从不截断它们。