python高级调试技巧——原生态的pdb调试

2023-01-10 08:56:46

声明:本文所讲的调试是指不附带任何工具的调试,我们平时使用vs code,pycharm进行调试,包括设置断点、单步执行、多步执行等操作都是IDE设置好的,本文不考虑这些,使用原生态的python调试器,不需要任何IDE开发环境。pdb是python自带的调试器,是python debugger 的简称。使用pdb调试有两种方式。

本次文章依然是分为系列文章进行讲解,本文为系列文章第一篇

目录

一,使用系统自带的pdb模块进行单步调试

二、使用set_trace()设置断点

三、pdb模块详解

四、使用ipython和jupyter notebook进行调试


一,使用系统自带的pdb模块进行单步调试

所谓的单步调试,就是指的是每次执行一句Python语句。 单步执行代码,通过在用户终端命令 python -m pdb xxx.py 启动脚本,进入单步执行模式。注意在终端命令输入的前面,会有这样一个显示:

(Pdb),这就表示进入了pdb命令调试。然后输入下面的命令即可。

注意:我们经常看见在python代码中会有 import pdb 这样的语句,但是这样的“单步执行模式”,是不需要在代码里面添加这句话的,当然天街也可以。

 pdb命令行:

    1)进入命令行Debug模式,python -m pdb xxx.py  这个格式是固定的

          之所以可以这样做,主要是因为pdb.py 可以被当做一个脚本执行。

    2)h:(help)帮助

    3)w:(where)打印当前执行堆栈

    4)d:(down)执行跳转到在当前堆栈的深一层(个人没觉得有什么用处)

    5)u:(up)执行跳转到当前堆栈的上一层

    6)b:(break)添加断点

                 b 列出当前所有断点,和断点执行到统计次数

                 b line_number:当前脚本的line_no行添加断点

                 b filename:line_number:脚本filename的line_no行添加断点

                 b function:在函数function的第一条可执行语句处添加断点

    7)tbreak:(temporary break)临时断点

                 在第一次执行到这个断点之后,就自动删除这个断点,用法和b一样

    8)cl:(clear)清除断点

                cl 清除所有断点

                cl bpnumber1 bpnumber2... 清除断点号为bpnumber1,bpnumber2...的断点

                cl line_number 清除当前脚本line_number行的断点

                cl filename:line_number 清除脚本filename的line_number行的断点

    9)disable:停用断点,参数为bpnumber,和cl的区别是,断点依然存在,只是不启用

    10)enable:激活断点,参数为bpnumber(即哪一个断点,1,2,3,4......)

    11)s:(step)执行下一条命令

                如果本句是函数调用,则s会执行到函数的第一句

    12)n:(next)执行下一条语句

                如果本句是函数调用,则执行函数,接着执行当前执行语句的下一条。

    13)r:(return)执行当前运行函数到结束

    14)c:(continue)继续执行,直到遇到下一条断点,这个比较重要,常常和断点结合起来使用。

    15)l:(list)列出源码

                 l 列出当前执行语句周围11条代码

                 l first 列出first行周围11条代码

                 l first second 列出first--second范围的代码,如果second<first,second将被解析为行数

    16)a:(args)列出当前执行函数的函数

    17)p expression:(print)输出expression的值

           比如:(Pdb) p 1+2

                   这里会打印出 3

          再比如:(Pdb) p c    #这里用来查看某个变量c的值

    18)pp expression:好看一点的p expression

    19)run:重新启动debug,相当于restart

    20)q:(quit)退出debug

    21)j lineno:(jump)设置下条执行的语句函数

                只能在堆栈的最底层跳转,向后重新执行,向前可直接执行到行号

    22)unt:(until)执行到下一行(跳出循环),或者当前堆栈结束

    23)condition bpnumber conditon,给断点设置条件,当参数condition返回True的时候bpnumber断点有效,否则bpnumber断点无效

注意:

    1:直接输入Enter,会执行上一条命令;

    2:输入PDB不认识的命令,PDB会把他当做Python语句在当前环境下执行;

    3:常用的命令有,(6)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)

二、使用set_trace()设置断点

pdb单步执行太麻烦了,所以第二种方法是import pdb 之后,直接在代码里需要调试的地方放一个pdb.set_trace(),就可以设置一个断点, 程序会在pdb.set_trace()暂停并进入pdb调试环境,可以用pdb 变量名查看变量,或者c继续运行。

如下代码:

import pdb  #导入这个模块

print('这是一个python调试程序')
def func1():
    for i in range(5):
        print(i)

def func2(a,b):
    return a/b

if __name__=='__main__':
    func1()
    pdb.set_trace()  #设置断点,程序会在调试的时候直接进入到这里暂停
    result=func2(3,0)
    print(result)

下面开始调试,

(base) C:\Users\lenovo>python F:\Python调试.py   #执行脚本
这是一个python调试程序
0
1
2
3
4     #自动执行到断点处
> f:\研究生二年级下\c#再学习\python调试\python调试\python调试.py(14)<module>()
-> result=func2(3,0)
(Pdb) n
ZeroDivisionError: division by zero
> f:\研究生二年级下\c#再学习\python调试\python调试\python调试.py(14)<module>()
-> result=func2(3,0)
(Pdb) n
--Return--
> f:\研究生二年级下\c#再学习\python调试\python调试\python调试.py(14)<module>()->None
-> result=func2(3,0)
(Pdb) n

总结:

使用set_trace()设置断点之后,调用脚本的时候,不再需要 -m pdb 这一块了,直接调用,然后就会进入调试模式,

几面会出现(Pdb),然后相关的执行命令和前面的是一样的。

三、pdb模块详解

1、pdb.set_trace()   设置断点

2、pdb.run(statement[, globals[, locals]])  ——主要用来调试代码块

     statement:需要进行调试的某一段代码,以字符串的形式给出

     globals: 设置statement运行的全局环境变量

     locals: 设置statement运行的局部环境变量

整体的作用类似于python内置模块的eval函数。

s='''a=100
b=20
c=0
d=a/b
e=d/c
'''

pdb.run(s)

现在运行上面这段代码,python Python调试.py

的搭配下面的结果:

(base) C:\Users\lenovo>python F:\研究生二年级下\C#再学习\Python调试\Python调试\Python调试.py
> <string>(1)<module>()
(Pdb) n
> <string>(2)<module>()
(Pdb) n
> <string>(3)<module>()
(Pdb) n
> <string>(4)<module>()
(Pdb) n
> <string>(5)<module>()
(Pdb) n
ZeroDivisionError: float division by zero
> <string>(5)<module>()

补充:

(1)globals:这个参数管控的是一个全局的命名空间,也就是我们在计算表达式的时候可以使用全局的命名空间中的函数,如果这个参数被提供了,并且没有提供自定义的__builtins__,那么会将当前环境中的__builtins__拷贝到自己提供的globals里,然后才会进行计算。关于__builtins__,它是python的内建模块,也就是python自带的模块,不需要我们import就可以使用的,例如我们平时使用的int、str、abs等都在这个模块中。关于它的说明可以参照这篇文章:点击打开链接。如果globals没有被提供,则使用python的全局命名空间。
(2)locals:这个参数管控的是一个局部的命名空间,和globals类似,不过当它和globals中有重复的部分时,locals里的定义会覆盖掉globals中的,也就是当globals和locals中有冲突的部分时,locals说了算,它有决定权,以它的为准。如果locals没有被 提供的话,则默认为globals。
 

3、pdb.runeval(expression,globals,locals)——主要用来调试表达式

expression也是以字符串的形式给出的,当表达式有返回值的时候,这个函数返回的值就是表达式所返回的值,如果没有返回值,那和上面的pdb.run()是一样的。

4、pdb.runcall(function[, argument, ...])——主要用来调试函数

对一个函数进行调试,注意,这里的function是一个函数或者是方法对象,不再是字符串哦,后面的是参数。当韩树有返回值的时候,函数的返回值就是runcall的返回值。

def myfunc(a,b,c):
    return (a/b)/c

result=pdb.runcall(myfunc,100,20,5)
print(result)

5、pdb的其他一些方法

除了上面几个常用的常用方法以外,还有以下几个常用方法,

pdb.post_mortem([traceback])

pdb.pm()

四、使用ipython和jupyter notebook进行调试

系列文章的下一篇会介绍,有兴趣的请关注。

 

  • 作者:LoveMIss-Y
  • 原文链接:https://blog.csdn.net/qq_27825451/article/details/85600992
    更新时间:2023-01-10 08:56:46