由于竞赛中只能使用Python解释器和Python原生的解释器来写代码,这两者都没有相应的调试功能,但是没想到吧!Python中内置了该功能。
笔者使用的python版本:3.8.10
PDB中的命令不止这些, 笔者这里只列出了一些笔者在使用过程中常用的命令、
初出茅庐
对于pdb的使用,我们首先要知道如何将其召唤出来。
pdb 的使用大部分是在cmd(命令提示符)中
在这里我们先创建一个python文件,并写入以下代码
import pdb
pdb.set_trace()
print("hello world")
这时候我们要开启一个cmd的窗口并进入到你的代码所在的文件夹中,执行以下的代码
python -m yourcode_file.py
接下来会出现一个这样的对话效果
【这是你的python文件的路径】>python -m test_codes.py
> 【这是路径】/test_codes.py(259)<module>()
-> print("hello world")
(Pdb)
出现这样的语句就说明你已经成功叫出了pdb调试交互窗口
难度提升
学会了如何调出pdb窗口,接下来我们就要开始学习其中的命令了。
上节中我们知道了如何将pdb窗口中调出来,和cmd一样都是一个黑框框,肯定是要去输入命令的。
这次,我们先从上节中的import开始说起
set_trace()
上节代码:
import pdb pdb.set_trace() print("hello world")
上节中我们调用了Python中的pdb库 ,并使用了其中的set_trace()方法
set_trace()就是添加断点的功能。
从上节中的执行效果来看的话,代码的执行在print语句的时候就停下来了。出现了一个箭头指向set_trace()语句的下一行。
-> print("hello world")
set_trace()这个语句添加的断点是给下一行用的。
程序执行的时候就会停在这个断点上,我们想干啥就可以干啥去了。
print()或者p
没错,就是print。
显而易见,该语句的作用就是打印目前的变量信息。
废话不多说,我们直接尝试一下
先向python文件中写入
import pdb
c = 1
pdb.set_trace()
a = 1
b = 2
pdb.set_trace()
c = a + b
print(c)
同样,我们执行
python -m your_file.py
进入到pdb界面以后,我们执行
(Pdb) print(c)
1
在这里我们使用另一种的打印命令也行,两者的效果是相同的
(Pdb)p c
如果我们尝试打印a的值的话
(Pdb) print(a)
这时候编译器显示
*** NameError: name ‘a’ is not defined
这是为什么呢?
因为添加的断点位置在赋值语句上,a还没有被赋值(在python中就是a变量还是不存在的)
这时候就要隆重介绍下一个语句:next语句
next或者n
next语句用于在断点的基础上向下执行语句。
我们还是使用上面的代码作为例子使用。
当执行到第一个断点的时候,使用next命令
(Pdb)next
> 【这是路径】\test_codes.py(260)<module>()
-> b = 2
(Pdb)
我们看到断点移动到了下一个语句中,而不是下一个断点中。
这时候我们再尝试打印一下变量a就可以用了。
(Pdb)p a
1
因为使用了next语句以后,python中就定义好了变量a,就可以正常使用了。
与上文中相同,next语句也可以被压缩成n来进行使用。
continue或者c
continue用于跳转到下一个断点处。
是不是听着和next的功能很像?且听详细分解。
| 名称 | 相同 | 不同 |
|---|---|---|
| continue | 都可以将代码直接执行到某一个点 | 执行到下一个断点 |
| next | - | 执行到下一行就截止 |
我们直接上实际的例子来好好感觉一下。
通过上面的next的例子我们知道,使用了next以后,代码跳转到了下一行,也就是
b = 2
这一行上。
现在使用continue试试。
(Pdb) continue
-> c = a + b
(Pdb)
从源代码中我们看到,使用continue直接执行到下一个断点处,也就是下一个set_trace()的地方。
list或者l
这个命令用于列出断点前后11行的代码,便于设立新的断点。
首先执行以上的代码,然后直接使用看看效果。
(Pdb) l
1 import pdb
2 pdb.set_trace()
3 -> a = 1
4 b = 2
5 pdb.set_trace()
6 c = a + b
7 print(c)
[EOF]
(Pdb)
将断点左右的代码都显示出来了。
代码的显示是有限制的,超过一定行数的将不会多余显示,显示功能仅针对于断点附近的代码。
where或者w
这段代码用来查看执行到断点的时候,程序中调用的堆栈信息。
说人话就是代码执行到这一步目前需要的函数/方法有哪些。
执行的效果:
(Pdb) where
c:\users\carro\appdata\local\programs\python\python38\lib\runpy.py(185)_run_module_as_main()
-> mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
c:\users\carro\appdata\local\programs\python\python38\lib\runpy.py(111)_get_module_details()
-> __import__(pkg_name)
<frozen importlib._bootstrap>(991)_find_and_load()
<frozen importlib._bootstrap>(975)_find_and_load_unlocked()
<frozen importlib._bootstrap>(671)_load_unlocked()
<frozen importlib._bootstrap_external>(848)exec_module()
<frozen importlib._bootstrap>(219)_call_with_frames_removed()
> g:\codes\python\test_codes.py(261)<module>()
-> pdb.set_trace()
(Pdb)
up或者u
up或者u该命令是continue的反向运用,两者的作用是相反的。
也就是跳到上一个断点处。
但是根据AI的描述是,该命令不存在。
注意:该命令能否使用还是未知。请慎用。
break或者b
该命令是BreakPoint的缩写。也就是——断点。但是与上文中的set_trace()不同的是,该方法设立的是该次运行中临时的断点。程序执行结束就没有了。
使用该命令可以为正在执行的代码加上断点,使用改命令创建的断点会返回一个断点的编号。
也就是为正在调试中的代码。
我们要更改一下上面的代码。
import pdb
pdb.set_trace()
a = 1
b = 2
# pdb.set_trace() # 将这段代码注释掉
c = a + b
print(c)
还是一样,我们在cmd中将代码跑起来。
python -m your_file.py
将现在的代码中加上临时的断点。
(Pdb) break 5
Breakpoint 1 【这是路径】\test_codes.py:5 # 这里有一个编号,也就是Breakpoint 1,这里的1就是断电的编号。
(Pdb) continue
> g:\codes\python\test_codes.py(261)<module>()
-> pdb.set_trace() # 将这段代码注释掉
(Pdb)
我们看到代码就跳到了代码中的第五行,也就是我们刚刚加上断点的地方。
step或者s
step命令用于跳入循环或者函数中。
我们要将以下写入python文件中,并在cmd中进行调用。
a = 1
for i in range(5):
a += 1
print(a)
省去调用的步骤,以下是结果。
-> a = 1
(Pdb) step
-> a = calc()
(Pdb) step
--Call--
-> def calc():
(Pdb)
如果我们使用的是next语句的话
-> a = 1
(Pdb) n
-> a = calc()
(Pdb) n
-> print(a)
(Pdb) n
11
==执行完成==
从两者中的对比我们不难看出,step的功能和next的功能是十分相似的,但是step会进入到函数/循环中,然后再接着一步步的执行。
| 函数名称 | 相同 | 不同 |
|---|---|---|
| next | 将代码向下执行一行 | 不进入函数或者循环中,遇到了直接使用其结果。 |
| step | — | 进入函数或者循环中,遇到了将进入并卡在其第一行上,相当于在其第一行设了一个断点 |
return或者r
step 的殿后者。
该命令将直接将程序的执行快进到函数(或循环)执行完毕的那一个语句上,将函数直接执行完毕。
-> a = 1
(Pdb) n
-> a = calc()
(Pdb) step
--Call--
-> def calc():
(Pdb) n
-> a = 1
(Pdb) n
-> for i in range(10):
(Pdb) n
-> a += 1
(Pdb) n
-> for i in range(10):
(Pdb) return
--Return--
> calc()->11
-> return a
(Pdb) n
-> print(a)
这样处理完成以后就不用在函数中一个一个跳了。
jump或者j
将程序的调试调回某一行或者快进到某一行。
我们还是使用上面的代码来进行验证一下。
要将之前加上去的注释去掉。
import pdb
c = 1
pdb.set_trace()
a = 1
b = 2
pdb.set_trace()
c = a + b
print(c)
我们执行代码以后跳到第二个断点处
-> a = 1
(Pdb) continue
-> c = a + b
(Pdb) l
259 c = 1
260 pdb.set_trace()
261 a = 1
262 b = 2
263 pdb.set_trace()
264 -> c = a + b
265 print(c)
[EOF]
(Pdb) jump 261
-> a = 1
(Pdb)
从list中的信息可以看到,我们断点卡在了264行,我们使用断点回调以后,调试语句回到了261行。
jump调试语句在使用的时候可能会导致无法想象的结果,例如前面的代码被重新执行了影响了后面的结果,或者后面的代码被执行以后导致了报错等等问题。
tbreak
临时断点pro版本
使用这种方法生成的断点在第一次被触发的时候就将会被移除(temporary breakpoint,也就是临时断点),下次到达此处的时候就不会再触发断点。
改断点的设置不能在文件中进行, 也是再pdb界面中使用的。
-> a = 1
(Pdb) l
256 # print(data.loc["2024-12-16 17:38:35", "生词"])
257
258 import pdb
259 c = 1
260 pdb.set_trace()
261 -> a = 1
262 b = 2
263 # pdb.set_trace()
264 c = a + b
265 print(c)
[EOF]
(Pdb) tbreak 264
Breakpoint 1 at g:\codes\python\test_codes.py:264
(Pdb) l
[EOF]
(Pdb) continue
Deleted breakpoint 1 at g:\codes\python\test_codes.py:264
> g:\codes\python\test_codes.py(264)<module>()
-> c = a + b
(Pdb) jump 261
> g:\codes\python\test_codes.py(261)<module>()
-> a = 1
(Pdb) l
256 # print(data.loc["2024-12-16 17:38:35", "生词"])
257
258 import pdb
259 c = 1
260 pdb.set_trace()
261 -> a = 1
262 b = 2
263 # pdb.set_trace()
264 c = a + b
265 print(c)
[EOF]
(Pdb) continue
3
我们看到执行了continue语句以后将原来创建的断点删除了。
这就是将该断点使用了以后自动删除的原理了。
condition
添加条件断点。
我们有的时候使用断点的时候,就是用来排查bug的。但是有的时候断点在循环中,循环要几次才能进入问题语句中,这时候我们就要使用条件断点来跳过前面的几次循环,直接干到问题循环中。
还是上面的代码,我们直接进入pdb调试控制台中操作。
-> a = 1
(Pdb) l
256 # print(data.loc["2024-12-16 17:38:35", "生词"])
257
258 import pdb
259 c = 1
260 pdb.set_trace()
261 -> a = 1
262 b = 2
263 # pdb.set_trace()
264 c = a + b
265 print(c)
[EOF]
(Pdb) break 264
Breakpoint 1 at g:\codes\python\test_codes.py:264
(Pdb) break 265
Breakpoint 2 at g:\codes\python\test_codes.py:265
(Pdb) break 261
Breakpoint 3 at g:\codes\python\test_codes.py:261
(Pdb) continue
> g:\codes\python\test_codes.py(264)<module>()
-> c = a + b
(Pdb) condition 2 if a == 2
New condition set for breakpoint 2.
(Pdb) conditoin 1 if b == 2
*** SyntaxError: invalid syntax
(Pdb) condition 1 if b == 2
New condition set for breakpoint 1.
(Pdb) continue
> g:\codes\python\test_codes.py(265)<module>()
-> print(c)
(Pdb) continue
3
从其中使用condition的语句中我们可以看到,使用condition语句要提供对于的断点的编号,还有将要使用的语句。
这里的条件既可以使用if语句,也可以直接使用判断的表达式。(就是把if去掉的那一部分)
在使用的时候要注意断点要先创建好,这样才能知道断点的编号是多少。
disable & enable [bpnumber]
这其实是两个命令,仔细来说,是两个作用相反的命令。
从两者中,我们很容易就知道,disable是用来禁用断点的,这时候断点就不能使用了。
听着是不是和删除很像?
| 名称 | 两者的区别 |
|---|---|
| 删除 | 将断点完全删除,后续无法恢复使用 |
| 禁用 | 将断点禁用,后续可以通过enable将断点重新启用。 |
从上文中我们也知道了enable的用法了。
但是两者对断点的操作都是使用的断点的编号,这意味着你在使用的时候要提前将断点创建好,这样才能-知道自己的断点的编号。
commands [bpnumber]
添加断点中需要执行的操作。
该命令用于设置当到达该断点的时候要执行的一系列操作(执行的是pdb中的命令,也就是上文和下文中介绍的命令)
设置的时候要加上断点的编号。
-> a = 1
(Pdb) l
258 import pdb
259 c = 1
260 pdb.set_trace()
261 -> a = 1
262 b = 2
263 # pdb.set_trace()
264 c = a + b
265 print(c)
[EOF]
(Pdb) break 264
Breakpoint 1 at g:\codes\python\test_codes.py:264
(Pdb) commands 1
(com) step
(Pdb) commands 1
(com) p a
(com) end
(Pdb) continue
1
> g:\codes\python\test_codes.py(264)<module>()
-> c = a + b
(Pdb)
断点的command的设置具有覆盖性,如果已经设置过command则会将之前设置的command覆盖掉,替换为最新的命令组。
从上文中的演示中我们也可以看到,我们使用的是end表示的命令组的结束。
但是使用step或者其他的**“具有继续执行的功能”**的功能的命令也有类似的功能。命令组结束以后就成功为断点设置了命令组。
whatis
查看变量的数据类型。
在该命令后面加上数据变量可以直接输出该变量的数据类型
-> a = 1
(Pdb) break 264
Breakpoint 1 at g:\codes\python\test_codes.py:264
(Pdb) continue
> g:\codes\python\test_codes.py(264)<module>()
-> c = a + b
(Pdb) whatis a
<class 'int'>
(Pdb) whatis b
<class 'int'>
(Pdb) whatis c
<class 'int'>
(Pdb)
如果变量不存在的话则是输出一个报错的结果。
display
记录变量的改变情况
display可以将变量的变化情况记录下来,分为现在的情况和之前的情况。
-> a = 1
(Pdb) display c
display c: 1
(Pdb) l
257
258 import pdb
259 c = 1
260 pdb.set_trace()
261 -> a = 1
262 b = 2
263 # pdb.set_trace()
264 c = a + b
265 print(c)
[EOF]
(Pdb) break 264
Breakpoint 1 at g:\codes\python\test_codes.py:264
(Pdb) continue
> g:\codes\python\test_codes.py(264)<module>()
-> c = a + b
(Pdb) next
> g:\codes\python\test_codes.py(265)<module>()
-> print(c)
display c: 3 [old: 1]
(Pdb)
从上文的代码中,我们可以看到在264行的时候c变量发生了改变。我们在这里打上断点,跳转下一行以后,display信息就出来了。
其中记录了c变量之前的值和现在的值。
display c: 3 [old: 1]
interact
该功能在python3.2的时候引入
创建一个新的编译器环境。
使用该命令以后,我们会得到一个新的python编译环境,该环境中的任何操作都不会影响到目前正在测试的代码。
-> a = 1
(Pdb) interact
*interactive*
>>> print("hello world")
hello world
>>>
笔者的python版本中,如果直接使用quit()或者exit()则会直接跳回cmd的界面中。
在python3.13中,在界面中使用quit()或者exit()跳出的是该环境,重新回到pdb调试界面中。
报错以后进行调试
使用命令
python -i <程序文件>
以后,我们就可以在报错以以后进入到python控制台中。
在控制台以后,我们首先要使用import语句导入pdb库。
import pdb
导入成功以后,我们再将使用pdb中的一个函数
pdb.pm()
这样我们就进入了pdb调试界面,该怎么操作就怎么操作吧。
提示:进入了界面以后,显示的断点是在程序报错的那个语句上。这里可以访问到报错之前的所有用到的变量。

6179

被折叠的 条评论
为什么被折叠?



