还用print在Python中debug吗?试试PySnooper吧!
print
对很多人来说算是最常用的debug神器了,只需要在合适的地方插入print
打印变量的值,就能判断代码在这个地方是不是还按照你的预期在运行。不过这也带来一些麻烦:首先需要找准位置,然后写出对应的print
语句。当函数复杂、变量多的时候确实挺烦的,通常需要多个地方插入多个变量的print
。
最近一个刚刚上线的Python包“PySnooper”更优雅地解决了这一需求——只需要对关注的函数前加上一个装饰器@pysnooper.snoop()
,就可以将这个函数运行的时间,行号、运行过程中变量的数值以及代码等内容输出到stderr。项目刚在GitHub上线1天左右,已经有超过1300个star,可以说是相当火爆了(更新:该项目成为2019.04.23 GitHub每日趋势榜第一名):
使用方法
安装很简单,直接使用pip
就行:
$ pip install pysnooper
使用也依然简单,我们这里写个简单的运算斐波那契数列的函数进行测试:
from __future__ import print_function
import pysnooper
@pysnooper.snoop()
def fib(n):
a, b = 0, 1
for i in xrange(n+1):
a, b = b, a + b
return a
def main():
for n in xrange(3):
print(fib(n), end=' ')
if __name__ == '__main__':
main()
运行程序就可以看到PySnooper的输出结果:
$ python mytest.py
Starting var:.. n = 0
00:10:16.244816 call 4 @pysnooper.snoop()
00:10:16.245712 line 6 a, b = 0, 1
New var:....... a = 0
New var:....... b = 1
00:10:16.245835 line 7 for i in xrange(n+1):
New var:....... i = 0
00:10:16.245926 line 8 a, b = b, a + b
Modified var:.. a = 1
00:10:16.245998 line 7 for i in xrange(n+1):
00:10:16.246048 line 9 return a
00:10:16.246115 return 9 return a
Starting var:.. n = 1
00:10:16.246228 call 4 @pysnooper.snoop()
00:10:16.246278 line 6 a, b = 0, 1
New var:....... a = 0
New var:....... b = 1
00:10:16.246358 line 7 for i in xrange(n+1):
New var:....... i = 0
00:10:16.246425 line 8 a, b = b, a + b
Modified var:.. a = 1
00:10:16.246491 line 7 for i in xrange(n+1):
Modified var:.. i = 1
00:10:16.246556 line 8 a, b = b, a + b
Modified var:.. b = 2
00:10:16.246620 line 7 for i in xrange(n+1):
00:10:16.246670 line 9 return a
00:10:16.246720 return 9 return a
Starting var:.. n = 2
00:10:16.246815 call 4 @pysnooper.snoop()
00:10:16.246865 line 6 a, b = 0, 1
New var:....... a = 0
New var:....... b = 1
00:10:16.246944 line 7 for i in xrange(n+1):
New var:....... i = 0
00:10:16.247012 line 8 a, b = b, a + b
Modified var:.. a = 1
00:10:16.247076 line 7 for i in xrange(n+1):
Modified var:.. i = 1
00:10:16.247140 line 8 a, b = b, a + b
Modified var:.. b = 2
00:10:16.247204 line 7 for i in xrange(n+1):
Modified var:.. i = 2
00:10:16.247268 line 8 a, b = b, a + b
Modified var:.. a = 2
Modified var:.. b = 3
00:10:16.247356 line 7 for i in xrange(n+1):
00:10:16.247404 line 9 return a
00:10:16.247453 return 9 return a
1 1 2
可以看到PySnooper输出了函数每条语句的运行时间(方便做性能优化测试)、变量的初始化赋值和修改后的值、行号、代码都进行了输出。最后输出的则是程序运行的结果。
自定义参数
PySnooper会输出到stderr。如果想要输出到文件,可以在命令行使用2> /my/log/file.log
重定向到文件。也可以给装饰器增加一个参数:
@pysnooper.snoop('/my/log/file.log')
如果想检查一些非局部变量,也可以使用参数variables
:
@pysnooper.snoop(variables=('foo.bar', 'self.whatever'))
通过参数depth
设定调用函数的深度:
@pysnooper.snoop(depth=2)
如果stderr和stdout一起输出可能会看不太清楚,可以通过prefix
参数给PySnooper的输出结果添加一个前缀,比如:
@pysnooper.snoop(prefix='ZZZ ')
一些不足
稍微做了一些测试之后也发现了不足:
- 即使加了
prefix
,输出看着还是有点乱。如果看着太烦,我可能还是宁愿用print
。可以类似IPython对输出内容用不同的颜色显示会更好。 - 只能针对
return
返回的函数使用,如果是使用yield
的生成器,似乎无法正常工作。 depth
参数只能设定调用其他函数的深度,但是无法设置递归函数的深度(调用自己),并且设定depth
超过1,还会引发报错(在调用其他函数时depth设定超过实际深度并不会报错)。
后记
后面这两点不足我在GitHub上提交了两个issues,作者表示欢迎pull requests。然而时间关系,只能等待有志之士来填坑了:
暂无评论