ipdb 使用小记
最近在魔改 loss function,涉及到很多矩阵运算,而矩阵运算中维度的对齐免不了要多次的调试;沿袭着之前的 print 大法弄了一段时间后,不仅代码凌乱不堪,而且心累:每次 import tensorflow as tf
都要十几秒,然后 print 完之后想进一步看其他变量的信息,又要重新执行一遍。后来找到了 ipdb 这个好用的工具,才发现自己过去调试程序的方法是多么的低效和 naive。
python 提供了一个默认的 debugger: pdb,而 ipdb 则是 pdb 的增强版,提供了补全、语法高亮等功能,类似于 ipython 与默认的 python 解释器的关系,通过 pip install ipdb
即可安装 ipdb。
使用方式
ipdb 的使用方法一般有两种: 集成到源代码或通过命令交互。
集成到源代码可以直接在代码指定位置插入断点。如下所示:
1 | import ipdb |
上面的代码会在执行完 var1 = 23
这条语句之后停止,展开 ipython 环境,之后就可以自由地调试了。
上面的方式虽然简单,但是存在着两个较为比较明显的问题:
- 插入的断点代码会污染原来的代码空间
- 每次插入断点都需要修改源码
因此,相比于上面的方式,交互式的命令式调试方法更加方便。启动命令式调试环境的方法也很简单:
python -m ipdb code.py
接着就是通过一些常用的命令来进行 debug了,如上面插入断点的样例代码就可以通过以下命令达到同样效果:
1 | $python -m ipdb code.py |
上面的命令 b 3
表示在第三行设置一个断点,然后通过命令 c
一直执行至断点处,接着就会展开 ipython 环境进行调试了。( b
和 c
分别代表了 break 和 continue,可以用整条命令,也可以只用首字母)
常用命令
上面的设置断点和一直执行至断点是比较常见的用法,除此之外,还有其他一些常用命令。
帮助
通过命令 h
可以列出所有命令,后面跟上具体的命令如 h command
则可以显示出这条命令的具体作用,非常有用,依靠这条命令能够节省不少 google 的次数。
断点
上面提到了断点的一种常见用法,即命令 b line_number
和 c
的组合, b line_number
默认是对当前文件设置断点,也可以在 line_number 前加上其他文件名(比如说要将要引用的其他文件),即 b file_name:line_number
;file_name 需要在 sys.path
中,当前目录已经默认存在 sys.path
, 也可通过 ..
引用上一层目录的文件。
另外, 通过 b
设置的断点在重新运行 debug 程序 (命令 restart
或 run
) 后会依然保留,如果要忽略这些断点,有两种做法
- 通过
disable
关闭这些断点,enable
打开这些断点 - 通过命令
clear
或cl
清除这些断点
此外,除了上面那种一直存在的断点,ipdb 中还有一种 只生效一次的断点,命令为 tbreak
, 使用方法同命令 b
。
如果需要列出已经设置的所有断点,可以单独使用命令 b
。
逐行执行
有两条命令可以进行逐行执行: s
(step) 或 n
(next), 两个命令的主要区别是: 假如当前行调用了某个函数, s
会进入这个函数, n
则不会。因此,如果需要了解函数内部执行的细节,需要 s
命令进入函数内部进行 debug。
进入了函数之后,通过命令 a
(argument) 可列出当前的函数的参数,通过 r
(return)则可以直接执行至 return 语句。
忽略某段代码
使用 j line_number
可以忽略某段代码,下一步直接从 line_number 开始执行。
查看源码
通过命令 l
或 ll
可查看源码, ll
是查看整个源码文件, l
可指定需要查看的行数,默认是当前往后 11 行,也可指定具体的范围,如 l 2,5
是查看第 2-5 行的源码。
重启或退出 debugger
上面已经提到了重启 debugger 可通过 restart
或 run
命令,需要注意的是, 重启 debugger 后断点、debugger 的设置等是会保留的。如果要一个全新的 debugger,可通过命令 q
、 quit
或 exit
退出 debugger 后进入。
小结
以上就是 ipdb 的一些基本用法,已经能满足大部分使用场景了;除此之外,一些其他更高级的命令(如跟踪或修改栈)可参考 pdb 的官方文档,ipdb 的命令跟 pdb 是一样的。另外,C/C++ 中的 gdb 也是一个类似的命令行 debugger,只是用来调试 C/C++ 而已,使用的模式相似,具体可参考 用GDB调试程序。