2.3.3 使用Python除错器

python除错器,pdb: http://docs.python.org/library/pdb.html, 允许你交互的检查代码。

具体来说,它允许你:

  • 查看源代码。
  • 在调用栈上下游走。
  • 检查变量值。
  • 修改变量值。
  • 设置断点。

print 是的,print语句确实可以作为除错工具。但是,要检查运行时间,使用除错器通常更加高效。

2.3.3.1 激活除错器

启动除错器的方式:

  • 事后剖析,在模块错误后启动除错器。
  • 用除错器启动模块。
  • 在模块中调用除错器。

2.3.3.1.1 事后剖析

情景: 你在IPython中工作时,你的到了一个traceback。

这里我们除错文件index_error.py。当运行它时,抛出IndexError。输入%debug进入除错器。

In [1]:

  1. %run index_error.py
  1.  ------------- ------------- ------------- ------------- -----------------------
  2. IndexError Traceback (most recent call last)
  3. /Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py in <module>()
  4. 6
  5. 7 if __name__ == '__main__':
  6. ----> 8 index_error()
  7. 9
  8. /Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py in index_error()
  9. 3 def index_error():
  10. 4 lst = list('foobar')
  11. ----> 5 print lst[len(lst)]
  12. 6
  13. 7 if __name__ == '__main__':
  14. IndexError: list index out of range

In [2]:

  1. %debug
  1. > /Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py(5)index_error()
  2. 4 lst = list('foobar')
  3. ----> 5 print lst[len(lst)]
  4. 6
  5. ipdb> list
  6. 1 """Small snippet to raise an IndexError."""
  7. 2
  8. 3 def index_error():
  9. 4 lst = list('foobar')
  10. ----> 5 print lst[len(lst)]
  11. 6
  12. 7 if __name__ == '__main__':
  13. 8 index_error()
  14. 9
  15. ipdb> len(lst)
  16. 6
  17. ipdb> print lst[len(lst)-1]
  18. r
  19. ipdb> quit

不用IPthon的事后剖析除错

在一些情况下,你不可以使用IPython,例如除错一个想到从命令行调用的脚本。在这个情况下,你可以用python -m pdb script.py调用脚本:

  1. $ python -m pdb index_error.py
  2. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/index_error.py(1)<module>()
  3. -> """Small snippet to raise an IndexError."""
  4. (Pdb) continue
  5. Traceback (most recent call last):
  6. File "/usr/lib/python2.6/pdb.py", line 1296, in main
  7. pdb._runscript(mainpyfile)
  8. File "/usr/lib/python2.6/pdb.py", line 1215, in _runscript
  9. self.run(statement)
  10. File "/usr/lib/python2.6/bdb.py", line 372, in run
  11. exec cmd in globals, locals
  12. File "<string>", line 1, in <module>
  13. File "index_error.py", line 8, in <module>
  14. index_error()
  15. File "index_error.py", line 5, in index_error
  16. print lst[len(lst)]
  17. IndexError: list index out of range
  18. Uncaught exception. Entering post mortem debugging
  19. Running 'cont' or 'step' will restart the program
  20. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/index_error.py(5)index_error()
  21. -> print lst[len(lst)]
  22. (Pdb)

2.3.3.1.2 逐步执行

情景:你相信模块中存在bug,但是你不知道在哪。

例如我们想要除错wiener_filtering.py。代码确实可以运行,但是,过滤不起作用。

  • 在IPython用%run -d wiener_filtering.p来运行这个脚本:
  1. In [1]: %run -d wiener_filtering.py
  2. *** Blank or comment
  3. *** Blank or comment
  4. *** Blank or comment
  5. Breakpoint 1 at /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py:4
  6. NOTE: Enter 'c' at the ipdb> prompt to start your script.
  7. > <string>(1)<module>()
  • b 34在34行设置一个断点:
  1. ipdb> n
  2. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(4)<module>()
  3. 3
  4. 1---> 4 import numpy as np
  5. 5 import scipy as sp
  6. ipdb> b 34
  7. Breakpoint 2 at /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py:34
  • c(ont(inue))继续运行到下一个断点:
  1. ipdb> c
  2. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(34)iterated_wiener()
  3. 33 """
  4. 2--> 34 noisy_img = noisy_img
  5. 35 denoised_img = local_mean(noisy_img, size=size)
  • n(ext)s(tep)在代码中步进:next在当前运行的背景下跳跃到下一个语句,而step将跨过执行的背景,即可以检查内部函数调用:
  1. ipdb> s
  2. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(35)iterated_wiener()
  3. 2 34 noisy_img = noisy_img
  4. ---> 35 denoised_img = local_mean(noisy_img, size=size)
  5. 36 l_var = local_var(noisy_img, size=size)
  6. ipdb> n
  7. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(36)iterated_wiener()
  8. 35 denoised_img = local_mean(noisy_img, size=size)
  9. ---> 36 l_var = local_var(noisy_img, size=size)
  10. 37 for i in range(3):
  • 跨过一些行,并且检查本地变量:
  1. ipdb> n
  2. > /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(37)iterated_wiener()
  3. 36 l_var = local_var(noisy_img, size=size)
  4. ---> 37 for i in range(3):
  5. 38 res = noisy_img - denoised_img
  6. ipdb> print l_var
  7. [[5868 5379 5316 ..., 5071 4799 5149]
  8. [5013 363 437 ..., 346 262 4355]
  9. [5379 410 344 ..., 392 604 3377]
  10. ...,
  11. [ 435 362 308 ..., 275 198 1632]
  12. [ 548 392 290 ..., 248 263 1653]
  13. [ 466 789 736 ..., 1835 1725 1940]]
  14. ipdb> print l_var.min()
  15. 0

哦,天啊,只有整合和0的变体。这就是我们的Bug,我们正在做整数算术。

在数字错误上抛出异常

当我们运行wiener_filtering.py文件时,将抛出下列警告:

In [3]:

  1. %run wiener_filtering.py
  1. wiener_filtering.py:40: RuntimeWarning: divide by zero encountered in divide
  2. noise_level = (1 - noise/l_var )

2.3.3 使用Python除错器 - 图1

2.3.3 使用Python除错器 - 图2

2.3.3 使用Python除错器 - 图3

我们可以将这些警告变成异常,这使我们可以做事后剖析对他们进行查错,更快的找到我们的问题:

In [5]:

  1. np.seterr(all='raise')

Out[5]:

  1. {'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'}

In [6]:

  1. %run wiener_filtering.py
  1.  ------------- ------------- ------------- ------------- -----------------------
  2. FloatingPointError Traceback (most recent call last)
  3. /Users/cloga/Documents/scipy-lecture-notes_cn/wiener_filtering.py in <module>()
  4. 55 pl.matshow(noisy_lena[cut], cmap=pl.cm.gray)
  5. 56
  6. ---> 57 denoised_lena = iterated_wiener(noisy_lena)
  7. 58 pl.matshow(denoised_lena[cut], cmap=pl.cm.gray)
  8. 59
  9. /Users/cloga/Documents/scipy-lecture-notes_cn/wiener_filtering.py in iterated_wiener(noisy_img, size)
  10. 38 res = noisy_img - denoised_img
  11. 39 noise = (res**2).sum()/res.size
  12. ---> 40 noise_level = (1 - noise/l_var )
  13. 41 noise_level[noise_level<0] = 0
  14. 42 denoised_img += noise_level*res
  15. FloatingPointError: divide by zero encountered in divide

2.3.3.1.3 启动除错器的其他的方式

  • 人为设置断点抛出异常

如果你发现记录行数设置断点很枯燥,那么你也可以直接在你想要检查的位置抛出异常然后使用IPython的%debug。注意这种情况下,你无法步进或继续这个异常。

  • 用nosetests除错测试失败

你可以运行nosetests —pdb来进入异常除错的事后剖析,运行nosetests —pdb-failure来用除错器检查失败的测试。

此外,你可以通过安装nose插件ipdbplugin来在nose中为除错器使用Ipython界面。然后为nosetest传递—ipdb—ipdb-failure选项。

  • 显性的调用除错器

在你想要进入除错器的地方插入下列几行:

In [ ]:

  1. import pdb; pdb.set_trace()

警告:当运行nosetests时, 会抓取输出,因此会感觉除错器没起作用。只要运行nosetests用-s标记。

图形化除错器和其他除错器

  • 对于在代码中步进和检查变量,你会发现用图形化除错器比如winpdb
  • 或者,pudb是优秀的半图形除错器,在控制台带有文字用户界面。
  • 同时,[pydbgr]项目可能也是值得一看的。

2.3.3.2 除错器命令和交互

l(list)列出当前位置的代码
u(p)在调用栈上向上走
d(own)在调用栈上向下走
n(ext)执行下一行代码(并不会进入新函数)
s(tep)执行下一个语句(并不会进入新函数)
bt打印调用栈
a打印本地函数
!command执行给到的Python命令(与pdb命令相对)

警告:除错器命令不是Python代码

你不能以想要的方式命名变量。例如,如果你无法在当前的框架下用相同的名字覆盖变量:用不同的名字,然后在除错器中输入代码时使用本地变量

2.3.3.2.1 在除错器中获得帮助

输入h或者help来进入交互帮助:

In [ ]:

  1. import pdb; pdb.set_trace()
  1. --Call--
  2. > /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/displayhook.py(222)__call__()
  3. -> def __call__(self, result=None):
  4. (Pdb) help
  5. Documented commands (type help <topic>):
  6. ========================================
  7. EOF bt cont enable jump pp run unt
  8. a c continue exit l q s until
  9. alias cl d h list quit step up
  10. args clear debug help n r tbreak w
  11. b commands disable ignore next restart u whatis
  12. break condition down j p return unalias where
  13. Miscellaneous help topics:
  14. ==========================
  15. exec pdb
  16. Undocumented commands:
  17. ======================
  18. retval rv