Python调试工具pdb使用详解

作者在学习自动化部署服务器的Python代码时,因无桌面服务器,选择使用pdb调试工具。文章介绍了pdb的基本信息,详细讲解了其命令行调试的各种命令,如进入调试模式、设置断点等,还提及了.pdbrc文件的使用,最后指出pdb在多线程、远程调试等方面的不足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言

  最近在实习公司刚拿到关于自动化部署服务器的Python代码,mentor让我先自己学习一下这个代码。这个代码主要是使用Python来使一些服务器节点能够自动化搭建产品的运行环境,所以代码需要放在Linux操作系统上运行。不过并没有一个带桌面的服务器使用,对于只会使用Pycharm来调试代码的我,突然觉得无从下手。后来想到c/c++有gdb调试工具,所以我也到网上搜索了一下,发现Python也有相似的调试工具—pdb.。因此通过自己的一些学习对pdb的相关使用方法进行总结。

2 参考文档

  pdb-Python调试器-Python3.7.4rc1文档
  17.8.pdb交互式调试器|《Python3标准库实例教程》|Python技术论坛

3 pdb简介

  pdb为Python程序定义了一个交互式源码调试工具。pdb支持在源码的行级别设置(条件)断点和单步执行以及进入函数调试、查看当前代码、查看栈片段、动态改变变量的值等。同时还支持事后调试,并可以在程序控制下调用。

4 pdb使用命令行调试

  pdb可以作为脚本调试其他脚本,进入调试模式指令:
  python3 –m pdb myscript.py
  命令行调试模式下,如果正在调试的程序异常退出,pdb将自动进入事后调试。在事后调试或者程序正常退出后,pdb将会重启程序,并会保留pdb状态(例如断点)。

4.1 举例代码

Test1.py

from Test2 import *

def test1_function1(list1, list2):
    content1 = "Test1 Function1 List1"
    list1.append(content1)
    content2 = "Test1 Function1 List2"
    list2.append(content2)

    test2_function1(list1, list2)

    finish_content = "Test1 Function1 Finished"
    list1.append(finish_content)
    list2.append(finish_content)


def test1_function2(list1, list2):
    test1_function1(list1, list2)

    content1 = "Test1 Function2 List1"
    list1.append(content1)
    content2 = "Test1 Function2 List2"
    list2.append(content2)

    finish_content = "Test1 Function2 Finished"
    list1.append(finish_content)
    list2.append(finish_content)


if __name__ == '__main__':
    list1 = ["List1 Main"]
    list2 = ["List2 Main"]
    test1_function2(list1, list2)
    print(list1)
    print(list2)

Test2.py


def test2_function1(list1, list2):
    content1 = "Test2 Function1 List1"
    list1.append(content1)
    content2 = "Test2 Function1 List2"
    list2.append(content2)

    loop_test(list1, list2)

    finish_content = "Test2 Function1 Finished"
    list1.append(finish_content)
    list2.append(finish_content)

def loop_test(list1 ,list2):
    for i in range(5):
        list1.append(i)
        list2.append(i)

4.2 调试器命令

  本文使用 Test1.pyTest2.py 讲解pdb相关命令
  以下列出调试器相关命令,大多数命令可以缩写为一个或者两个字母,如h(elp)表示h或者help都可以使用命令的功能。命令的参数必须使用空格或制表符分隔,[ ]方括号中的参数表示可选参数,使用可选参数时不需要键入方括号。当输入空行时会执行上一条命令。

4.2.1 进入pdb调试模式

(1) 进入pdb命令行调试模式 : python –m pdb Test1.py

从命令行运行调试器时载入代码并停止在程序的第一个声明

进入pdb调试模式

4.2.2 帮助指令

(2) h(elp) [command]

没有参数时,打印可用命令列表,使用命令作为参数时,则打印该命令的相关帮助。

help不带参数指令help带参数指令

4.2.3 控制程序执行

(3) s(tep)

执行当前行,在当前函数的下一行或者调用函数内的第1条语句停止。

step指令
(4) n(ext)

执行当前行,并在当前函数的下一行停止,与step类似但不进入正在执行的语句调用的函数,next指令会跳过函数执行的细节。

next指令
(5) unt(il) [lineno]

   如果没有参数,则继续执行代码,直到代码行数大于当前行数,所以没有参数时,与next指令功能相同。使用参数,指定行号,则执行程序直到达到大于或等于该数字的行数的代码处。该指令可用于跳至循环的结束。如果行号大于当前函数行号的范围,则会在当前函数返回时停止。

until指令
(6) r(eturn)

程序继续执行,直到当前函数返回时或即将执行return语句时停止。便于在函数返回之前查看返回值。

return指令
(7) c(ont(inue))

程序继续执行,直到遇到断点时停止。

continue指令
(8) j(ump) lineno

  设置将要执行的下一行。jump命令在运行时改变程序的流程,而不修改代码。 它可以向前跳过以避免运行某些代码,也可以向后跳转以再次运行它。向前跳转会将执行点移动到当前位置之后,而不会执行其间的任何语句。跳转还可以将程序执行移动到已执行的语句,以再次运行它。
  跳入和退出某些流控制语句是危险的或未定义的,因此调试器不允许这样做。不能跳转到for循环的中间或跳出finally子句。

jump指令

4.2.4 设置断点

(9) b(reak) [([filename:]lineno | function) [, condition]]

  使用lineno参数时,表示在当前文件指定行号处设置断点。使用function参数时可在该函数的第一条可执行语句处设置断点。行号还可以带有文件名和冒号前缀,以指定另一个文件的断点,注意文件应在sys.path路径中。每创建一个断点,则为断点分配一个所有其他断点命令可引用的数字bpnumber。
  如果使用第二个参数condition,则它应该是一个表达式,在断点被接受之前该表达式的值应为true。
  如果没有参数,则列出所有断点的相关信息。

break指令
(10) tbreak[([filename:]lineno | function) [, condition]]

  临时断点在程序执行第一次命中时会自动清除。 使用临时断点可以很容易地快速到达程序流程中的特定位置,就像使用常规断点一样,但是由于它会立即清除,因此如果程序的该部分重复运行,则不会干扰后续过程。参数设置与break相同。

tbreak指令
(11) cl(ear) [filename:lineno | bpnumber[bpnumber...]]

  使用filename:lineno参数时,清除此行的断点,使用以空格分隔的断点编号为参数时,清除指定编号的断点,没有参数时清除所有断点(需要确认)。

clear指令
(12) disable [bpnumber [bpnumber...]]

禁用以空格分隔的断点编号,禁用断点将会使断点失效无法停止程序,但与清除断点不同,它会保留断点并可以重新启用。

(13) enable [bpnumber [bpnumber...]]

启用指定的断点。

enable&disable
(14) ignore bpnumber [count]

  使用循环或使用对同一函数的大量递归调用的程序时,通常可以通过在执行中跳过来更容易地进行调试,而不是观察每个调用或断点。ignore命令告诉调试器在不停止的情况下跳过断点。 每次处理遇到断点时,它都会减少忽略计数器。 当计数器为零时,重新激活断点。当count参数省略时,默认为0。

ignore指令
(15) condition bpnumber [condition]

  为断点设置一个新条件,这个表达式必须在断点被接受之前计算为true。如果condition不存在,则删除任何现有条件,即将有条件的断点设置为无条件断点。condition参数必须是使用在定义断点的堆栈帧中可见的值的表达式。与break的condition参数用法相同。

condition

4.2.5 查看函数堆栈

(16) w(here)

可用来确切地找出正在执行的行以及程序所在的调用堆栈的位置,最近的帧位于底部。箭头表示当前帧。

(17) d(own)

在堆栈中向下移动帧,移动至较新的帧。

(18) u(p)

向堆栈中较旧的帧移动。每次向上或者向下移动堆栈时,调试器都会以where相同的格式打印当前位置。

where&down&up指令

4.2.6 查看堆栈上的变量

(19) a(rgs)

打印当前函数的参数。

(20) p expression

计算作为参数的表达式并打印结果。

(21) pp expression

与p命令一致,区别是使用pprint模块进行漂亮的打印

args&p&pp指令
(22) whatis expression

打印表达式的类型
whatis指令

(23) display [expression]

每次执行在当前帧中停止时,显示表达式的值(如果已更改)。如果没有表达式,列出当前帧的所有显示表达式。

(24) undisplay [expression]

清除在当前帧中活动的显示表达式。如果没有表达式,清除当前帧的所有活动的显示表达式。

display&undisplay指令

4.2.7 查看源码

(25) l(ist) [first[, last]]

在当前位置周围添加更多上下文,默认设置是列出当前行周围的 11 行(前五行和后五行)。 使用单个数字参数时列出指定行周围的 11 行而不是当前行。如果 list 使用两个参数时,它会显示指定范围的代码。

(26) ll | longlist
命令打印当前函数或帧的源,而不必事先确定行号。 函数源码较多的,它可能打印比 list 的默认值更多的代码。
list&longlist指令
(27) source expression

获取并打印类,函数,或模块的完整源代码

source

4.2.8 交互式操作

(28) commands [bpnumber]

  使用commands可以在遇到特定断点时执行一系列解释器命令,包括Python语句。执行commands,调试器提示符变为(com)。以此输入一个命令,并以end结束以保存脚本并返回主调试器提示符。如果没有bpnumber参数,cammands指向最后一个断点。可通过continue、step、next、return、jump、quit以及它们的缩写终止命令列表(已输入的代码无效)。

commands指令
(29) interact

  启动一个交互式解释器,其中已填充当前帧中的全局变量和局部变量。可以从交互式解释器更改 list 等可变对象。 不可变对象不能改变,并且名称不能绑定到新值。使用文件结束序列 Ctrl + z ,并回车退出交互式提示并返回调试器。

Interact指令
(30) !statement

pdb调试器允许在当前函数中执行Python语句,如果该Python语句类似与调试器指令,可以在前加上感叹号,表示将表达式传递给Python解释器。

4.2.9 自定义调试器别名

(31) alias [name [command]]

  使用 alias 定义快捷方式,以避免重复键入复杂命令。别名扩展应用于每个命令的第一个单词。 别名的主体可以包含在调试器提示符下输入合法的任何命令,包括其他调试器命令和纯 Python 表达式。 别名定义允许递归,因此一个别名甚至可以调用另一个别名。
  不带任何参数运行 alias 会显示已定义别名的列表。
  单个参数时打印指定的别名。
  使用 %n 引用别名的参数,其中 n 被替换为表示参数位置的数字,从 1 开始。 要使用所有参数,请使用%*。

(32) unalias name

清除一个别名的定义。

alias和unalias指令

4.2.10 重启pdb调试

(33) run [args ...]

  当调试器到达程序结束时,它会自动将其重新启动,但也可以显式重新启动,而无需离开调试器并丢失当前断点或其他设置。.run 重新启动程序。 传递给 run 的参数用 shlex 解析并传递给程序,好像它们是命令行参数一样,因此可以使用不同的设置重新启动程序。

(34) restart [args ...]

与run指令用法一致,restart是run的别名。

4.2.11 退出pdb调试

(35) q(uit)

退出调试器。正在执行的程序被中止。

5 使用.pdbrc文件

  调试程序包含了大量重复操作:运行代码、观察输出、调整代码或输入,然后再次运行。 pdb 试图减少所需的重复次数来控制调试体验,让你专注于代码而不是调试器。 为了减少向调试器发出相同命令的次数, pdb 可以在启动时读取解释的文本文件中保存的配置。
  进入pdb调试模式时,首先读取文件 ~/.pdbrc (环境变量“HOME”路径下的.pdbrc文件),所有项目进入pdb调试模式时都会读取的配置文件。 然后读取 ./.pdbrc(当前目录下的.pdbrc文件),特定项目才会读取的配置文件。

  • windows下新建.pdbrc文件
      在当前目录下进入cmd,输入 echo >.pdbrc,即可创建.pdbrc文件。
    创建pdbrc文件

  • 打开.pdbrc文件,输入想要执行的指令,并保存。

    n
    n
    n
    b 33
    b 10
    c
    c
    a
    p list1
    ll
    
  • 进入pdb调试模式 python3 -m pdb Test1.py
    使用.pdbrc文件进入pdb调试模式

6 pdb调试工具的不足

  pdb 调试有个明显的缺陷就是对于多线程,远程调试等支持得不够好,同时没有较为直观的界面显示,不太适合大型的 python 项目。而在较大的 python 项目中,这些调试需求比较常见,因此需要使用更为高级的调试工具比如pycharm。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值