deBug Python 代码再也不需要 print 了。只要给有疑问的代码加上装饰器,各种信息一目了然,找出错误也就非常简单了。

项目地址:https://github.com/cool-RR/pysnooper

PySnooper 案例

下面项目作者写了一个函数以将数值转换为二进制码,该函数返回的是一个二进制列表。下面我们将装饰器 @pysnooper.snoop() 加到该函数上,就大功告成了。

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)

该函数返回的日志如下,我们可以看到在调用 number_to_bits 函数时,赋予参数 number 的初始值为 6。然后,PySnooper 就还是对着源代码一行行分析了。

Starting var:.. number = 6
21:14:32.099769 call         3 @pysnooper.snoop()
21:14:32.099769 line         5     if number:
21:14:32.099769 line         6         bits = []
New var:....... bits = []
21:14:32.099769 line         7         while number:
21:14:32.099769 line         8             number, remainder = divmod(number, 2)
New var:....... remainder = 0
Modified var:.. number = 3
21:14:32.099769 line         9             bits.insert(0, remainder)
Modified var:.. bits = [0]
21:14:32.099769 line         7         while number:
21:14:32.099769 line         8             number, remainder = divmod(number, 2)
Modified var:.. number = 1
Modified var:.. remainder = 1
21:14:32.099769 line         9             bits.insert(0, remainder)
Modified var:.. bits = [1, 0]
21:14:32.099769 line         7         while number:
21:14:32.099769 line         8             number, remainder = divmod(number, 2)
Modified var:.. number = 0
21:14:32.099769 line         9             bits.insert(0, remainder)
Modified var:.. bits = [1, 1, 0]
21:14:32.099769 line         7         while number:
21:14:32.099769 line        10         return bits
21:14:32.099769 return      10         return bits

如上分析所示,函数每创建一个新变量,那么这个变量的值、这个变量的变化都会展示出来。而且 PySnooper 还将循环展开,因此变化的细节更加明确。最终 6 的二进制版本应该是 [1, 1, 0],它的变化过程也展示在 bits 变量中。

PySnooper 详细特征

如果标准错误输出难以获得,或者太长了,那么可以将输出定位到本地文件:

@pysnooper.snoop('/my/log/file.log')

查看一些非本地变量的值:

@pysnooper.snoop(variables=('foo.bar', 'self.whatever'))

展示我们函数中调用函数的 snoop 行:

@pysnooper.snoop(depth=2)

将所有 snoop 行以某个前缀开始,更容易定位和找到:

@pysnooper.snoop(prefix='ZZZ ')