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]:
%run index_error.py
------------- ------------- ------------- ------------- -----------------------
IndexError Traceback (most recent call last)
/Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py in <module>()
6
7 if __name__ == '__main__':
----> 8 index_error()
9
/Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py in index_error()
3 def index_error():
4 lst = list('foobar')
----> 5 print lst[len(lst)]
6
7 if __name__ == '__main__':
IndexError: list index out of range
In [2]:
%debug
> /Users/cloga/Documents/scipy-lecture-notes_cn/index_error.py(5)index_error()
4 lst = list('foobar')
----> 5 print lst[len(lst)]
6
ipdb> list
1 """Small snippet to raise an IndexError."""
2
3 def index_error():
4 lst = list('foobar')
----> 5 print lst[len(lst)]
6
7 if __name__ == '__main__':
8 index_error()
9
ipdb> len(lst)
6
ipdb> print lst[len(lst)-1]
r
ipdb> quit
不用IPthon的事后剖析除错
在一些情况下,你不可以使用IPython,例如除错一个想到从命令行调用的脚本。在这个情况下,你可以用python -m pdb script.py
调用脚本:
$ python -m pdb index_error.py
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/index_error.py(1)<module>()
-> """Small snippet to raise an IndexError."""
(Pdb) continue
Traceback (most recent call last):
File "/usr/lib/python2.6/pdb.py", line 1296, in main
pdb._runscript(mainpyfile)
File "/usr/lib/python2.6/pdb.py", line 1215, in _runscript
self.run(statement)
File "/usr/lib/python2.6/bdb.py", line 372, in run
exec cmd in globals, locals
File "<string>", line 1, in <module>
File "index_error.py", line 8, in <module>
index_error()
File "index_error.py", line 5, in index_error
print lst[len(lst)]
IndexError: list index out of range
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/index_error.py(5)index_error()
-> print lst[len(lst)]
(Pdb)
2.3.3.1.2 逐步执行
情景:你相信模块中存在bug,但是你不知道在哪。
例如我们想要除错wiener_filtering.py。代码确实可以运行,但是,过滤不起作用。
- 在IPython用
%run -d wiener_filtering.p
来运行这个脚本:
In [1]: %run -d wiener_filtering.py
*** Blank or comment
*** Blank or comment
*** Blank or comment
Breakpoint 1 at /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py:4
NOTE: Enter 'c' at the ipdb> prompt to start your script.
> <string>(1)<module>()
- 用
b 34
在34行设置一个断点:
ipdb> n
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(4)<module>()
3
1---> 4 import numpy as np
5 import scipy as sp
ipdb> b 34
Breakpoint 2 at /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py:34
- 用
c(ont(inue))
继续运行到下一个断点:
ipdb> c
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(34)iterated_wiener()
33 """
2--> 34 noisy_img = noisy_img
35 denoised_img = local_mean(noisy_img, size=size)
- 用
n(ext)
和s(tep)
在代码中步进:next
在当前运行的背景下跳跃到下一个语句,而step
将跨过执行的背景,即可以检查内部函数调用:
ipdb> s
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(35)iterated_wiener()
2 34 noisy_img = noisy_img
---> 35 denoised_img = local_mean(noisy_img, size=size)
36 l_var = local_var(noisy_img, size=size)
ipdb> n
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(36)iterated_wiener()
35 denoised_img = local_mean(noisy_img, size=size)
---> 36 l_var = local_var(noisy_img, size=size)
37 for i in range(3):
- 跨过一些行,并且检查本地变量:
ipdb> n
> /home/varoquau/dev/scipy-lecture-notes/advanced/debugging_optimizing/wiener_filtering.py(37)iterated_wiener()
36 l_var = local_var(noisy_img, size=size)
---> 37 for i in range(3):
38 res = noisy_img - denoised_img
ipdb> print l_var
[[5868 5379 5316 ..., 5071 4799 5149]
[5013 363 437 ..., 346 262 4355]
[5379 410 344 ..., 392 604 3377]
...,
[ 435 362 308 ..., 275 198 1632]
[ 548 392 290 ..., 248 263 1653]
[ 466 789 736 ..., 1835 1725 1940]]
ipdb> print l_var.min()
0
哦,天啊,只有整合和0的变体。这就是我们的Bug,我们正在做整数算术。
在数字错误上抛出异常
当我们运行wiener_filtering.py
文件时,将抛出下列警告:
In [3]:
%run wiener_filtering.py
wiener_filtering.py:40: RuntimeWarning: divide by zero encountered in divide
noise_level = (1 - noise/l_var )
我们可以将这些警告变成异常,这使我们可以做事后剖析对他们进行查错,更快的找到我们的问题:
In [5]:
np.seterr(all='raise')
Out[5]:
{'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'}
In [6]:
%run wiener_filtering.py
------------- ------------- ------------- ------------- -----------------------
FloatingPointError Traceback (most recent call last)
/Users/cloga/Documents/scipy-lecture-notes_cn/wiener_filtering.py in <module>()
55 pl.matshow(noisy_lena[cut], cmap=pl.cm.gray)
56
---> 57 denoised_lena = iterated_wiener(noisy_lena)
58 pl.matshow(denoised_lena[cut], cmap=pl.cm.gray)
59
/Users/cloga/Documents/scipy-lecture-notes_cn/wiener_filtering.py in iterated_wiener(noisy_img, size)
38 res = noisy_img - denoised_img
39 noise = (res**2).sum()/res.size
---> 40 noise_level = (1 - noise/l_var )
41 noise_level[noise_level<0] = 0
42 denoised_img += noise_level*res
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 [ ]:
import pdb; pdb.set_trace()
警告:当运行nosetests时, 会抓取输出,因此会感觉除错器没起作用。只要运行nosetests用-s标记。
图形化除错器和其他除错器
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 [ ]:
import pdb; pdb.set_trace()
--Call--
> /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython/core/displayhook.py(222)__call__()
-> def __call__(self, result=None):
(Pdb) help
Documented commands (type help <topic>):
========================================
EOF bt cont enable jump pp run unt
a c continue exit l q s until
alias cl d h list quit step up
args clear debug help n r tbreak w
b commands disable ignore next restart u whatis
break condition down j p return unalias where
Miscellaneous help topics:
==========================
exec pdb
Undocumented commands:
======================
retval rv