什么是pom? pom作为项目对象模型。通过xml表示maven项目,使用pom.xml来实现。主要描述了项目:包括配置文件;开发者需要遵循的规则,缺陷管理系统,组织和licenses,项目的url,项目的依赖性,以及其他所有的项目相关因素。 快速察看: xml 代码 <project> <modelVersion>4.0.0</modelVersion> <groupId>...</groupId> <artifactId>...</artifactId>…
Python: Python调试器, Python代码调试, pdb使用手册, 10分钟教程掌握Python调试器pdb, 零基础学习PDB, Python dubug
pdb 是 python 自带的一个包,为 python 程序提供了一种交互的源代码调试功能,主要特性包括设置断点、单步调试、进入函数调试、查看当前代码、查看栈片段、动态改变变量的值等。
如果你还主要靠print来调试代码,那值得花10分钟试试pdb这个Python自带的Debug工具。
PDB有3种用法:
- 非侵入式方法(不用额外修改源代码,在命令行下直接运行就能调试)
python3 -m pdb filename.py
- Python交互环境中启用
>>> import pdb >>> import mymodule >>> pdb.run('mymodule.test()')
- 侵入式方法(需要在被调试的代码中添加一行代码然后再正常运行代码)
import pdb pdb.set_trace()
当你在命令行看到下面这个提示符时,说明已经正确打开了pdb
(Pdb)
然后就可以开始输入pdb命令了,下面是pdb的常用命令
QuickStart
待调试的代码内容
文件名:test.py:
# coding:utf-8 import pdb s1 = 'aaa' pdb.set_trace() s2 = 'bbb' s3 = 'ccc' pdb.set_trace() s = s1 + s2 + s3 print(s)
可以看出在代码的第4、7行分别打了一个断点,使用的是pdb.set_trace()
函数。
开始调试
在和代码文件相同路径下打开命令行窗口,输入命令:python test1.py
接着就进入了调试状态:
(.env) E:\code\python-basic\tools\pdb\sample>python test1.py > e:\code\python-basic\tools\pdb\sample\test1.py(5)<module>() -> s2 = 'bbb' (Pdb)
可以看出直接执行到了第一个断点所在的下一行,并停在了这里。
这时可以执行命令:n
进行下一步:
(Pdb) n > e:\code\python-basic\tools\pdb\sample\test1.py(6)<module>() -> s3 = 'ccc' (Pdb)
使用p <变量名>
命令打印已经出现过的变量的值:
(Pdb) p s1 'aaa' (Pdb) p s2 'bbb' (Pdb) p s3 *** NameError: NameError("name 's3' is not defined",) (Pdb)
因为当前变量s3还没有被赋值,所以打印s3的时候提示NameError
异常。
使用l
命令打印出当前的代码段:
(Pdb) l 1 # coding:utf-8 2 import pdb 3 s1 = 'aaa' 4 pdb.set_trace() 5 s2 = 'bbb' 6 -> s3 = 'ccc' 7 pdb.set_trace() 8 s = s1 + s2 + s3 9 print(s) [EOF] (Pdb)
退出调试:q
命令
(Pdb) q Traceback (most recent call last): File "test1.py", line 6, in <module> s3 = 'ccc' File "test1.py", line 6, in <module> s3 = 'ccc' File "d:\programs\python27\Lib\bdb.py", line 49, in trace_dispatch return self.dispatch_line(frame) File "d:\programs\python27\Lib\bdb.py", line 68, in dispatch_line if self.quitting: raise BdbQuit bdb.BdbQuit (.env) E:\code\python-basic\tools\pdb\sample>
PDB调试的另一种方式
QuickStart中使用的调试方式不够优雅,因为是通过修改代码的方式打断点的,用起来不太方便。那么能不能动态打断点呢?答案是当然可以,请接着往下看。
准备待调试的代码
删除掉QuickStart中代码中的pdb.set_trace()
,剩下的代码如下:
文件名:test2.py
# coding:utf-8 s1 = 'aaa' s2 = 'bbb' s3 = 'ccc' s = s1 + s2 + s3 print(s)
开始调试
在test2.py相同路径下打开命令行,输入命令:python -m pdb test2.py
(.env) E:\code\python-basic\tools\pdb\sample>python -m pdb test2.py > e:\code\python-basic\tools\pdb\sample\test2.py(2)<module>() -> s1 = 'aaa' (Pdb) l 1 # coding:utf-8 2 -> s1 = 'aaa' 3 s2 = 'bbb' 4 s3 = 'ccc' 5 s = s1 + s2 + s3 6 print(s) [EOF] (Pdb)
可以看到当前代码中我们还没有打任何断点,代码默认停在了第1行。
执行一个命令n
:
(Pdb) n > e:\code\python-basic\tools\pdb\sample\test2.py(3)<module>() -> s2 = 'bbb' (Pdb) l 1 # coding:utf-8 2 s1 = 'aaa' 3 -> s2 = 'bbb' 4 s3 = 'ccc' 5 s = s1 + s2 + s3 6 print(s) [EOF] (Pdb)
可以看到单步执行到了下一行。
如果我们想在第5行打一个断点,该怎么打呢?用b <行号>
命令在某一行打一个断点:
(Pdb) b 5 Breakpoint 1 at e:\code\python-basic\tools\pdb\sample\test2.py:5 (Pdb) l [EOF] (Pdb) n > e:\code\python-basic\tools\pdb\sample\test2.py(4)<module>() -> s3 = 'ccc' (Pdb) l 1 # coding:utf-8 2 s1 = 'aaa' 3 s2 = 'bbb' 4 -> s3 = 'ccc' 5 B s = s1 + s2 + s3 6 print(s) [EOF] (Pdb)
这样就成功地在第5行打了一个断点。
查看当前打了哪些断点:b
命令
(Pdb) b Num Type Disp Enb Where 1 breakpoint keep yes at e:\code\python-basic\tools\pdb\sample\test2.py:5 (Pdb)
PDB调试命令汇总
高级命令
以上的示例只是展示了最简单的顺序结构的代码的调试方法,而实际应用中遇到的大多数代码都有着较为复杂的逻辑结构,比如循环结构、分支结构、调用函数、调用其他模块的函数、使用类和对象等等。
针对这些场景还有很多更高级的调试命令,其实掌握了前面的几个简单的命令的用法后,下面的这些更高级的命令就都很容易上手了,多用几遍就能很快掌握了。
命令 | 命令全称 | 功能 |
---|---|---|
h | help | 查看帮助 |
n | next | 执行下一条语句 |
s | step | 执行下一条语句,如果是函数,则会执行到函数的第一句 |
b | break | 列出当前的所有断点 |
b <行号> | / | 在某一行打一个断点 |
b <文件名>:<行号> | / | 在某个文件的某行打一个断点 |
b <函数名> | / | 在某个函数的第一行打一个断点 |
cl | clear | 清除所有断点 |
cl n1 n2 … | / | 清除编号为n1、n2…的断点 |
cl <行号> | / | 清除某行的断点 |
cl <文件名>:<行号> | / | 清除某个文件某行的断点 |
r | return | 执行当前函数到结束 |
c | continue | 执行到下一个断点 |
l | list | 列出源码(前后11行代码) |
l <行号> | / | 列出某行周围11行代码 |
l <行号1> <行号2> | / | 列出两个行号范围内的代码 |
p <变量名> | print <变量名> | 输出变量的值 |
pp <变量名> | / | 好看一点的输出 |
q | quit | 退出debug |
unt | until | 退出循环或当期堆栈 |
run | / | 重新启动debug |
a | args | 列出当前执行的函数的参数 |
w | where | 打印当前执行堆栈 |
注:平时使用的时候通常用的都是各个命令的简写形式,当然用全称也是可以的(如果不嫌麻烦的话)。
补充
- 在命令行中进入调试模式的方法:
python -m pdb demo.py
- 在调试模式中按一下
Enter
键表示执行一下上一条命令。
更多实例:
1. 退出 debug:使用 quit 或者 q 可以退出当前的 debug,但是 quit 会以一种非常粗鲁的方式退出程序,其结果是直接 crash。
[root@rcc-pok-idg-2255 ~]# python epdb1.py > /root/epdb1.py(4)?() -> b = "bbb" (Pdb) n > /root/epdb1.py(5)?() -> c = "ccc" (Pdb) q Traceback (most recent call last): File "epdb1.py", line 5, in ? c = "ccc" File "epdb1.py", line 5, in ? c = "ccc" File "/usr/lib64/python2.4/bdb.py", line 48, in trace_dispatch return self.dispatch_line(frame) File "/usr/lib64/python2.4/bdb.py", line 67, in dispatch_line if self.quitting: raise BdbQuit bdb.BdbQuit
2. 打印变量的值:如果需要在调试过程中打印变量的值,可以直接使用 p 加上变量名,但是需要注意的是打印仅仅在当前的 statement 已经被执行了之后才能看到具体的值,否则会报 NameError: < exceptions.NameError … ….> 错误。
[root@rcc-pok-idg-2255 ~]# python epdb1.py > /root/epdb1.py(4)?() -> b = "bbb" (Pdb) n > /root/epdb1.py(5)?() -> c = "ccc" (Pdb) p b 'bbb' (Pdb) 'bbb' (Pdb) n > /root/epdb1.py(6)?() -> final = a + b + c (Pdb) p c 'ccc' (Pdb) p final *** NameError: <exceptions.NameError instance at 0x1551b710 > (Pdb) n > /root/epdb1.py(7)?() -> print final (Pdb) p final 'aaabbbccc' (Pdb)
3. 使用 c 可以停止当前的 debug 使程序继续执行。如果在下面的程序中继续有 set_statement() 的申明,则又会重新进入到 debug 的状态,读者可以在代码 print final 之前再加上 set_trace() 验证。
[root@rcc-pok-idg-2255 ~]# python epdb1.py > /root/epdb1.py(4)?() -> b = "bbb" (Pdb) n > /root/epdb1.py(5)?() -> c = "ccc" (Pdb) c aaabbbccc
4. 显示代码:在 debug 的时候不一定能记住当前的代码块,如要要查看具体的代码块,则可以通过使用 list 或者 l 命令显示。list 会用箭头 -> 指向当前 debug 的语句。
[root@rcc-pok-idg-2255 ~]# python epdb1.py > /root/epdb1.py(4)?() -> b = "bbb" (Pdb) list 1 import pdb 2 a = "aaa" 3 pdb.set_trace() 4 -> b = "bbb" 5 c = "ccc" 6 final = a + b + c 7 pdb.set_trace() 8 print final [EOF] (Pdb) c > /root/epdb1.py(8)?() -> print final (Pdb) list 3 pdb.set_trace() 4 b = "bbb" 5 c = "ccc" 6 final = a + b + c 7 pdb.set_trace() 8 -> print final [EOF] (Pdb)
5. 在使用函数的情况下进行 debu
import pdb def combine(s1,s2): # define subroutine combine, which... s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ... s3 = '"' + s3 +'"' # encloses it in double quotes,... return s3 # and returns it. a = "aaa" pdb.set_trace() b = "bbb" c = "ccc" final = combine(a,b) print final
6. 如果直接使用 n 进行 debug 则到 final=combine(a,b) 这句的时候会将其当做普通的赋值语句处理,进入到 print final。如果想要对函数进行 debug 如何处理呢 ? 可以直接使用 s 进入函数块。函数里面的单步调试与上面的介绍类似。如果不想在函数里单步调试可以在断点处直接按 r 退出到调用的地方。
[root@rcc-pok-idg-2255 ~]# python epdb2.py > /root/epdb2.py(10)?() -> b = "bbb" (Pdb) n > /root/epdb2.py(11)?() -> c = "ccc" (Pdb) n > /root/epdb2.py(12)?() -> final = combine(a,b) (Pdb) s --Call-- > /root/epdb2.py(3)combine() -> def combine(s1,s2): # define subroutine combine, which... (Pdb) n > /root/epdb2.py(4)combine() -> s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ... (Pdb) list 1 import pdb 2 3 def combine(s1,s2): # define subroutine combine, which... 4 -> s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ... 5 s3 = '"' + s3 +'"' # encloses it in double quotes,... 6 return s3 # and returns it. 7 8 a = "aaa" 9 pdb.set_trace() 10 b = "bbb" 11 c = "ccc" (Pdb) n > /root/epdb2.py(5)combine() -> s3 = '"' + s3 +'"' # encloses it in double quotes,... (Pdb) n > /root/epdb2.py(6)combine() -> return s3 # and returns it. (Pdb) n --Return-- > /root/epdb2.py(6)combine()->'"aaabbbaaa"' -> return s3 # and returns it. (Pdb) n > /root/epdb2.py(13)?() -> print final (Pdb)
7. 在调试的时候动态改变值 。在调试的时候可以动态改变变量的值,具体如下实例。需要注意的是下面有个错误,原因是 b 已经被赋值了,如果想重新改变 b 的赋值,则应该使用! B。
[root@rcc-pok-idg-2255 ~]# python epdb2.py > /root/epdb2.py(10)?() -> b = "bbb" (Pdb) var = "1234" (Pdb) b = "avfe" *** The specified object '= "avfe"' is not a function or was not found along sys.path. (Pdb) !b="afdfd" (Pdb)
开源的ipdb
ipdb是一个开源的Python调试器,它和pdb有相同的接口,但是,它相对于pdb,具有语法高亮、tab补全、更友好的堆栈信息等高级功能。ipdb之于pdb,就相当于IPython之于Python,虽然都是实现相同的功能,但是,在易用性方面做了很多的改进。如下所示:
需要注意的是,pdb是Python的标准库,不用安装就可以直接使用。而ipdb是一个第三方的库,因此,需要使用pip先安装,然后才能使用:
pip install ipdb
将我们前面的例子,改为使用ipdb进行调试以后,代码就变成了下面这样:
from __future__ import print_function
import ipdb
def sum_nums(n):
s=0
for i in range(n):
ipdb.set_trace()
s += i
print(s)
if __name__ == '__main__':
sum_nums(5)
除了使用pdb和ipdb以外,如果读者使用PyCharm进行编程,则可以使用PyCharm的图形界面进行调试。PyCharm的图形界面的使用和显示都更加友好,几乎是傻瓜式操作。
本文:Python: Python调试器, Python代码调试, pdb使用手册, 10分钟教程掌握Python调试器pdb, 零基础学习PDB