python自产调试工具pdb的使用
介绍
- 调试打印在写代码的时候不可避免
- 项目越大,调试可能花的时间会越多
- print调试可能是最早用的,一段时间内你都会习惯这种方式
- 一旦成了老鸟,你应该会去用IDE的debugger,功能非常强大,效率就比print上了一个台阶
- 当然python像其他语言一样,也有自己的调试工具,pdb
- python debugger:目的无非是为了知道程序此时的状态(在做啥,环境信息,当然它还要能暂停你的程序
调测代码
-
题目
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 示例 1: 输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9,返回 [0, 1] 。 示例 2: 输入:nums = [3,2,4], target = 6 输出:[1,2] 示例 3: 输入:nums = [3,3], target = 6 输出:[0,1] 提示: # 下面的这些不考虑 2 <= nums.length <= 104 -109 <= nums[i] <= 109 -109 <= target <= 109 只会存在一个有效答案 进阶:你可以想出一个时间复杂度小于 O(n2 的算法吗?
-
我写个错误的代码
demo_twosum.py
class Solution: def twoSum(self, nums: List[int], target: int -> List[int]: for i,v in enumerate(nums: if target-v in nums: return [i,nums.index(target-v]
-
对于以下cases,应返回[1,2],实际返回[0,0]
输入:nums = [3,2,4], target = 6
-
因为题目中有要求:
数组中同一个元素在答案里不能重复出现
pdb调试
切入pdb的方式
from typing import List
class Solution:
def twoSum(self, nums: List[int], target: int -> List[int]:
for i,v in enumerate(nums:
if target-v in nums:
return [i,nums.index(target-v]
nums = [3,2,4]
target = 6
solution = Solution(.twoSum(nums,target
print(solution
你可以执行
python -m pdb demo_twosum.py
执行界面大致如下
# python -m pdb demo_twosum.py
> demo_twosum.py(1<module>(
-> from typing import List
你还可以在代码中加breakpoint(
if target-v in nums:
breakpoint( # 加在你要调试的代码附近 Line 7
return [i,nums.index(target-v]
运行你的代码提示如下
D:\Python39\python.exe demo_twosum.py
> demo_twosum.py(7twoSum(
-> return [i,nums.index(target-v]
(Pdb
常用的调试命令
典型的操作
D:\pythonProject\外部公开课\当前公开课>python -m pdb demo_twosum.py
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(1<module>(
-> from typing import List
(Pdb b 5
Breakpoint 1 at d:\pythonproject\外部公开课\当前公开课\demo_twosum.py:5
(Pdb c
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5twoSum(
-> if target-v in nums:
(Pdb w
d:\python39\lib\bdb.py(580run(
-> exec(cmd, globals, locals
<string>(1<module>(
d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(9<module>(
-> solution = Solution(.twoSum(nums,target
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5twoSum(
-> if target-v in nums:
(Pdb p i
0
(Pdb p target
6
(Pdb p v
3
(Pdb r
--Return--
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(6twoSum(->[0, 0]
-> return [i,nums.index(target-v]
(Pdb l
1 from typing import List
2 class Solution:
3 def twoSum(self, nums: List[int], target: int -> List[int]:
4 for i,v in enumerate(nums:
5 B if target-v in nums:
6 -> return [i,nums.index(target-v]
7 nums = [3,2,4]
8 target = 6
9 solution = Solution(.twoSum(nums,target
10 print(solution
[EOF]
(Pdb p [i,nums.index(target-v]
[0, 0]
(Pdb q
所以,这是在做啥?
第1行
D:\pythonProject>python -m pdb demo_twosum.py
> d:\pythonproject\demo_twosum.py(1<module>(
-> from typing import List
-
当前文件绝对路径 d:\pythonproject\demo_twosum.py
-
是个module
<module>(
-
参考上面
breakpoint(
的方式,你会发现显示的是你打了断点下面的那一行代码。-> return [i,nums.index(target-v]
第2行
(Pdb b 5
Breakpoint 1 at d:\pythonproject\外部公开课\当前公开课\demo_twosum.py:5
- 在第5行设置一个断点
clear 断点编号
就可以取消该断点第3行
(Pdb c
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5twoSum(
-> if target-v in nums:
- 运行到设置的断点处
第4行
(Pdb w
d:\python39\lib\bdb.py(580run(
-> exec(cmd, globals, locals
<string>(1<module>(
d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(9<module>(
-> solution = Solution(.twoSum(nums,target
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5twoSum(
-> if target-v in nums:
- 查看当前的调用栈
第5行
(Pdb p i
0
(Pdb p target
6
(Pdb p v
3
打印当前的变量
注意p可以打印任意有效的python表达式
# 比如 任意的python 语句
v = 3
第6行
(Pdb r
--Return--
> d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(6twoSum(->[0, 0]
-> return [i,nums.index(target-v]
- 运行到返回
第7行
(Pdb l
- 打印当前位置前后11行代码
l.
可以列出当前行周围的 11 行第8行:打印[i,nums.index(target-v]
,发现是[0,0]
附录 pdb命令全解
h(elp [command]
command 时,打印有关该命令的帮助。
help pdb
显示完整文档(即pdb
模块的文档字符串)。由于 command 参数必须是标识符,因此要获取!
的帮助必须输入help exec
。
w(here
d(own [count]
count 级(默认为 1 级,移向更新的帧)。
u(p [count]
count 级(默认为 1 级,移向更老的帧)。
b(reak [([filename:]lineno | function [, condition]]
lineno 参数,则在当前文件相应行处设置一个断点。如果带有 function 参数,则在该函数的第一条可执行语句处设置一个断点。行号可以加上文件名和冒号作为前缀,以在另一个文件(可能是尚未加载的文件)中设置一个断点。另一个文件将在 sys.path
范围内搜索。请注意,每个断点都分配有一个编号,其他所有断点命令都引用该编号。如果第二个参数存在,它应该是一个表达式,且它的计算值为 true 时断点才起作用。如果不带参数执行,将列出所有中断,包括每个断点、命中该断点的次数、当前的忽略次数以及关联的条件(如果有)。
tbreak [([filename:]lineno | function [, condition]]
break 相同。
cl(ear [filename:lineno | bpnumber ...]
filename:lineno,则清除此行上的所有断点。如果参数是空格分隔的断点编号列表,则清除这些断点。如果不带参数,则清除所有断点(但会先提示确认)。
disable [bpnumber ...]
enable [bpnumber ...]
ignore bpnumber [count]
condition bpnumber [condition]
condition,它是一个表达式,且它的计算值为 true 时断点才起作用。如果没有给出 condition,则删除现有条件,也就是将断点设为无条件。
commands [bpnumber]
bpnumber 的断点指定一系列命令。命令内容将显示在后续的几行中。输入仅包含 end
的行来结束命令列表。举个例子:(Pdb commands 1 (com p some_variable (com end (Pdb
要删除断点上的所有命令,请输入 commands
并立即以 end
结尾,也就是不指定任何命令。如果不带 bpnumber 参数,commands
作用于最后一个被设置的断点。可以为断点指定命令来重新启动程序。只需使用 continue
或 step
命令或其他可以继续运行程序的命令。如果指定了某个继续运行程序的命令(目前包括 continue
, step
, next
, return
, jump
, quit
及它们的缩写)将终止命令列表(就像该命令后紧跟着 end)。因为在任何时候继续运行下去(即使是简单的 next 或 step),都可能会遇到另一个断点,该断点可能具有自己的命令列表,这导致要执行的列表含糊不清。如果在命令列表中加入 'silent' 命令,那么在该断点处停下时就不会打印常规信息。如果希望断点打印特定信息后继续运行,这可能是理想的。如果没有其他命令来打印一些信息,则看不到已达到断点的迹象。
s(tep
n(ext
next 和
step
之间的区别在于,step
进入被调用函数内部并停止,而next
(几乎)全速运行被调用函数,仅在当前函数的下一行停止。)
unt(il [lineno]
在 3.2 版更改: 允许明确给定行号。
r(eturn
c(ont(inue
j(ump lineno
for 循环的中间或跳出
finally
子句。
l(ist [first[, last]]
. 作为参数,则列出当前行周围的 11 行。如果带有一个参数,则列出那一行周围的 11 行。如果带有两个参数,则列出所给的范围中的代码;如果第二个参数小于第一个参数,则将其解释为列出行数的计数。当前帧中的当前行用 ->
标记。如果正在调试异常,且最早抛出或传递该异常的行不是当前行,则那一行用 >>
标记。3.2 新版功能: >>
标记。
ll | longlist
list 相同。3.2 新版功能.
a(rgs
p expression
expression 并打印它的值。注解
print(
也可以使用,但它不是一个调试器命令 --- 它执行 Pythonprint(
函数。
pp expression
p 命令类似,但表达式的值使用 pprint
模块美观地打印。
whatis expression
expression 的类型。
source expression
3.2 新版功能.
display [expression]
3.2 新版功能.
undisplay [expression]
3.2 新版功能.
interact
code 模块),它的全局命名空间将包含当前作用域中的所有(全局和局部)名称。3.2 新版功能.
alias [name [command]]
name 的别名来执行 command。 执行的命令 不可 加上引号。 可替换形参可通过 %1
, %2
等来标示,而 %*
会被所有形参所替换。 如果没有给出命令,则会显示 name 的当前别名。 如果没有给出参数,则会列出所有别名。别名允许嵌套并可包含能在 pdb 提示符下合法输入的任何内容。 请注意内部 pdb 命令 可以 被别名所覆盖。 这样的命令将被隐藏直到别名被移除。 别名会递归地应用到命令行的第一个单词;行内的其他单词不会受影响。作为示例,这里列出了两个有用的别名(特别适合放在 .pdbrc
文件中):# Print instance variables (usage "pi classInst" alias pi for k in %1.__dict__.keys(: print("%1.",k,"=",%1.__dict__[k] # Print instance variables in self alias ps pi self
unalias name
! statement
statement。 感叹号可以被省略,除非语句的第一个单词与调试器命令重名。 要设置全局变量,你可以在同一行上为赋值命令添加前缀的
global
语句,例如:(Pdb global list_options; list_options = ['-l'] (Pdb
run [args ...]
restart [args ...]
shlex 来拆分且拆分结果将被用作新的 sys.argv
。 历史、中断点、动作和调试器选项将被保留。 restart
是 run
的一个别名。
q(uit
debug code
retval
打印函数最后一次返回的返回值。