Python原生pdb实现零侵入断点 终结传统print排错盲区

【python】来学学debugger吧,不能只会用print调试呀! — 码农高天

码农高天

相比反复插入print语句,pdb无需修改源码即可穿透标准库与第三方依赖,并通过运行时堆栈导航与动态篡改内存状态大幅提升复杂程序的排错效率。

AI-assisted summary of 【python】来学学debugger吧,不能只会用print调试呀! — original source. Check the original for full context.

Source published:

Key ideas

  • 调试器的核心理念可归结为两大功能:在程序运行到关键节点时暂停,并在暂停时全方位检视与干预程序状态,其效率与灵活性远超传统的 print 调试。 (0:00)
  • Python 原生提供 breakpoint() 内置函数与 pdb 交互工具,支持自上而下的调用栈回溯(where/up/down)与源码上下文速览(ll)。 (0:00)
  • pdb 支持 next、step、until 等细粒度流程控制,并允许在调试交互行中直接执行 Python 语句修改内存变量,主动验证排错假设。 (2:37)
  • 通过 python -m pdb 命令行启动或针对函数名设断点(如 b inspect.currentframe),可在完全不侵入源码的前提下直接追踪第三方库或 Python 标准库底层逻辑。 (6:06)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2024 年 6 月 22 日发布。视频简介指出,本期内容主要讲解 Python 自带的调试器 pdb 的基础用法与调试器的基本核心概念,旨在帮助开发者在 print 调试遇到瓶颈时掌握更高效的排错工具。

## TL;DR 视频系统性地拆解了任何调试器的两大核心本质:在指定位置暂停程序,以及在暂停时检视与干预程序状态。作者首先演示了如何使用内置的 breakpoint() 函数中断程序,并通过 p、w、l、ll 以及调用栈切换命令(u/d)检视上下文。随后,视频深入讲解了流程控制技巧,包括 n(单步跳过)、s(单步进入)、until(跳出循环)与运行时动态修改变量值。最后,作者展示了如何使用 python -m pdb 进行零代码侵入调试,以及如何直接针对函数名(如第三方库或内置库)打断点以深入探查底层源码。

## Atmosphere & style 整期视频呈现出极为扎实、干练的技术教学风格,没有多余的寒暄,直奔终端命令行与代码实操。码农高天的讲解语调沉稳自然、条理清晰,善于站在初学者的痛点上对比 print 与 debugger 的思维差异。演示过程行云流水,命令输入与调用栈反馈即时呈现,给观众带来一种揭开程序黑盒、将执行流完全掌控在手心的踏实感与掌控感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 调试器的核心理念可归结为两大功能:在程序运行到关键节点时暂停,并在暂停时全方位检视与干预程序状态,其效率与灵活性远超传统的 print 调试。 - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) Python 原生提供 breakpoint() 内置函数与 pdb 交互工具,支持自上而下的调用栈回溯(where/up/down)与源码上下文速览(ll)。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) pdb 支持 next、step、until 等细粒度流程控制,并允许在调试交互行中直接执行 Python 语句修改内存变量,主动验证排错假设。 - [06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) 通过 python -m pdb 命令行启动或针对函数名设断点(如 b inspect.currentframe),可在完全不侵入源码的前提下直接追踪第三方库或 Python 标准库底层逻辑。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 调试的核心目标是搞清程序在特定时刻的实际行为,所有调试器的底层逻辑都围绕“暂停程序”与“检视状态”展开。 - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) Python 3.7+ 提供了内置函数 breakpoint(),在有源码修改权限时,无需导入任何模块即可直接让程序停在指定位置并唤出 pdb 交互界面。 - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 使用 w(where)命令可查看自上而下的调用栈,配合 u(up)与 d(down)命令能够在不同调用帧之间自由切换,直接检视外层或内层函数的局部作用域。 - [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 在查看上下文源码时,l(list)可分批翻页查看附近代码,而 ll(long list)能直接完整输出当前函数的全部源码。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) n(next,单步步过)与 s(step,单步步入)的区别仅在遇到函数调用时显现:前者将函数直接执行完毕跳至下一行,后者则会跟进函数内部并增加一层调用栈。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) 在被调函数执行即将结束时,pdb 会发出返回提示,此时执行 retval 命令可直接打印该函数的返回值。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) 针对耗时的循环结构,until 命令(支持附带目标行号)可使程序持续运行至大于当前行号的代码处,从而快速脱离循环回跳。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) 在深入函数后若无需继续逐行执行,输入 r(return)命令即可直接执行到函数即将返回的前一刻。 - [02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) pdb 不仅是被动的检视器,还允许在提示符下直接执行合法的 Python 语句来修改内存中的变量数据,用于即时验证修复方案。 - [06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) 当面对无法修改的源码或排查标准库、第三方库时,使用 python -m pdb 启动脚本可实现零源码侵入的断点调试。 - [06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) b(break)断点命令不仅支持指定行号,更支持直接以函数名或模块函数名(如 b inspect.currentframe)为参数,命中后直接跳入底层库代码执行现场。 - [06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) 通过不带参数的 break 命令可罗列当前全部断点及编号,使用 clear <编号> 可单独移除断点,输入 q(quit)可随时终止调试退出程序。 - [06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) 主流语言调试器的核心机制高度相通,建立基于调用栈与流程控制的 debugger 思维,能从根本上摆脱复杂工程中 print 调试的局限性。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 调试器核心理念与检视程序状态(pdb 入门)

项目越复杂,开发者投入在调试(debugging)上的时间就越多。绝大多数人最习惯的调试方式是使用 `print`,虽然 `print` 很有效,但在很多复杂场景下,熟练使用调试器(debugger)的效率会高得多。pdb 是 Python 官方自带的命令行调试器。从本质上说,无论哪种调试器,核心目标都是弄清楚程序在特定时刻正在干什么。因此,任何调试器的核心功能都可以归结为两块:第一,在开发者感兴趣的地方暂停程序;第二,在程序暂停时检视程序的状态。

如果开发者对源代码具有修改权限,在 Python 中最推荐的方式是在感兴趣的地方直接插入内置函数 `breakpoint()`。运行代码后,程序便会停在断点处并进入 pdb 的交互命令行界面(提示符为 `(Pdb)`)。命令行输入的上方通常会显示两行上下文信息:第一行显示当前执行位置(包括文件的绝对路径、代码所在行号以及所处的函数名,例如 `括号 6 代表第 6 行,f 代表在 f 函数里`);第二行则是即将要执行的代码内容。例如在第 5 行写了 `breakpoint()`,程序就会停在第 6 行之前,准备执行第 6 行。

```mermaid flowchart TD accTitle: pdb 状态检视命令工作流 accDescr: 展示在 pdb 断点暂停后,如何使用 print、list、where 以及堆栈移动命令检视状态。 subgraph S1["程序中断"] A["遇到 breakpoint() 暂停"] --> B["显示当前行号、函数与即将执行的代码"] end subgraph S2["状态检视常用命令"] B --> C["p / print: 打印变量或表达式"] B --> D["w / where: 查看调用栈"] B --> E["l / list / ll: 查看源码上下文"] end subgraph S3["堆栈帧切换"] D --> F["u / up: 向上切换至调用者帧"] F --> G["d / down: 向下切换回被调用帧"] end ```

程序暂停后即可开始检视状态。最基本的操作是打印变量,可以使用 `p`(即 `print`)命令,输入 `p x` 便会打印出变量的值(如 `3`)。另一个高频使用的命令是查看调用栈(call stack),输入 `w` 或 `where` 即可显示当前的调用栈层级。Python 的调用栈是从上往下打印调用链路的,左侧会有一个小箭头指示当前所在的栈帧(frame)。若想查看断点附近的代码,可以使用 `l`(`list`)命令,默认会打印当前行前后的 11 行代码,再次输入 `l` 会继续向下翻 11 行,输入 `l.`(list 点)则会跳回当前执行行;此外还有一个极其好用的命令 `ll`(`long list`),它能直接完整展示当前函数的全部源代码。当需要检查外层函数的变量时,可以通过 `u`(`up`)命令在调用栈中向上移动一层栈帧,此时局部变量作用域随之改变;查看完毕后,再通过 `d`(`down`)命令向下调整回内层栈帧。

码农高天强调,学会使用 debugger 能极大改变排错思维:“我们所谓的 debug 无非就是想知道这个程序在这个时候在干什么,因此任何一个 debugger 无非就是两大块的核心功能:第一是在我感兴趣的地方暂停程序,第二是在暂停程序的时候去检视程序的状态。”

- `breakpoint()`:Python 3.7+ 原生内置断点函数,无需导入模块即可直接中断进入 pdb。 - `p <expr>` / `print <expr>`:求值并打印变量或合法的 Python 表达式。 - `w` / `where`:打印当前的完整函数调用栈(从上至下调用)。 - `l` / `list`:列出当前代码行附近的上下文源码;再次执行向后翻页;`l.` 重置回当前执行行。 - `ll` / `long list`:直接展示当前所在函数的全部代码。 - `u` / `up` 与 `d` / `down`:在调用栈的不同栈帧(frames)之间上下移动,切换当前的局部变量作用域。

---

[02:37](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=157s) 控制程序流与运行时动态修改

调试器除了检视状态,核心在于精确控制程序的执行流。当程序准备执行下一行代码时,可以使用 `n`(`next`)命令单步执行一行。例如在初始化列表之后、进入 `for` 循环之前执行 `n`,列表就被成功创建,随后进入循环获取迭代变量。当遇到函数调用(如 `v = g(...)`)时,如果继续输入 `n`,调试器会把整个函数直接执行完毕并将返回值赋给变量跳到下一行;如果想要深入探究该函数内部的细节,则必须使用 `s`(`step`)命令进入(step into)该函数。进入函数后,调用栈就会增加一层。

在控制流中,`next` 和 `step` 的分水岭仅在于当前行是否包含函数调用:“大家注意一下 next 和 step 只有在这一行代码里有函数调用的时候才不一样。在有函数调用的时候,next 的意思是把这个函数直接调用完然后到下一行,而 step 的意思是进入这个函数的调用。”

在函数执行即将结束时,pdb 会显式提示函数即将返回,此时可以通过 pdb 特有的 `retval` 命令直接打印本次调用的返回值。回到循环结构中,普通单步调试每次遇到循环体结尾都会跳回循环开头;若想快速跳出循环,可以使用 `until` 命令。`until` 不带参数时的语义是“一直运行,直到执行到的代码行号大于当前行号”,能直接摆脱循环回跳;`until` 还可以接收一个行号作为参数(如 `until 10`),指示调试器一直执行到行号大于等于 10 为止,从而整体跳过整个循环体。若处于函数内部想直接执行到返回前夕,可使用 `r`(`return`)命令。

pdb 极为强大的特性在于允许开发者在调试提示符下直接运行任意合法的 Python 语句来修改运行时状态。例如可以在中断时输入 `list = [1, 2, 3]` 强行重置变量内容,后续循环便会基于这个新列表继续运行。这证明调试器不仅能够被动“检视”程序,还能主动“篡改”程序状态来验证假设。当调试完毕需要让程序恢复全速运行时,输入 `c`(`continue`)命令即可让代码一直跑下去直到结束或遇到下一个断点。

```mermaid flowchart TD accTitle: pdb 单步执行与流程控制决策 accDescr: 展示在遇到函数调用或循环时,next、step、until 和 return 的行为差异。 A{"当前行是否包含函数调用?"} A -- "是" --> B{"是否想进入函数内部?"} B -- "想深入内部" --> C["使用 s (step) 进入函数"] B -- "直接拿结果" --> D["使用 n (next) 执行完整行"] A -- "否" --> D

subgraph 快速跳出逻辑 E{"处于何种结构?"} E -- "在循环中" --> F["使用 until [行号] 跳出循环"] E -- "在函数中" --> G["使用 r (return) 运行至函数返回前"] end ```

- `n`(`next`):单步步过(step over),将当前行整体执行完毕,移至下一行。 - `s`(`step`):单步步入(step into),遇到函数调用时停在被调函数的第一行。 - `retval`:在被调用函数执行到返回点(return)时,打印其返回值。 - `until [line_number]`:继续运行直到行号大于当前行,或行号大于等于指定参数,常用于跳出循环。 - `r`(`return`):单步步出(step out),继续运行直到当前函数即将返回。 - 动态修改:可在 pdb 中直接执行赋值等语句(如 `list = [1, 2, 3]`)即时改变内存数据。 - `c`(`continue`):继续全速运行程序,直到遇到下一个断点或程序终止。

---

[06:06](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=366s) 零源码侵入调试、外部库探查与断点管理

当开发者面对无法修改的代码、或者排查第三方库(甚至 Python 内置库)内部的 bug 时,传统的 `print` 完全无能为力。此时 pdb 提供了极大的灵活性,有两类主要途径:第一种是在自己的代码里插入 `breakpoint()`,执行到外部调用时利用 `s`(`step`)强行单步跟进第三方库代码;第二种是完全不修改任何源码,直接通过命令行模块启动 pdb:

```bash python -m pdb your_script.py ```

以此方式启动时,pdb 会自动在程序第一个可以暂停的地方(例如模块加载和定义函数的第一行)暂停。为了避免从头一行行按 `n` 漫长步进,开发者可以在命令行中手动设置断点。设置断点的命令为 `b`(即 `break`)。例如输入 `b 5`,即在当前文件第 5 行打上断点;若直接输入不带参数的 `break`,则会列出当前已设置的所有断点及其编号。设置断点后配合 `c`(`continue`),程序便会直接飞跃至目标行暂停,完全不需要修改磁盘上的任何文件。调试结束如需中途强行退出,输入 `q`(`quit`)即可。

```mermaid flowchart LR accTitle: 命令行启动 pdb 与断点调试流程 accDescr: 展示无需修改源码下,通过 python -m pdb 启动并基于行号或函数名设置断点的调试过程。 A["python -m pdb script.py 启动"] --> B["在脚本第 1 行自动暂停"] B --> C["设置断点: b <行号> 或 b <函数名>"] C --> D["输入 c (continue) 飞跃执行"] D --> E["在断点处命中暂停,开启状态检视"] E --> F["clear <编号> 删除断点 / q (quit) 退出调试"] ```

`b` 命令不仅支持指定行号,更支持直接以“函数名”作为参数,这一特性在调试库代码时非常有用。码农高天演示了一个调用标准库 `inspect.currentframe()` 的例子:若想知道这个内置函数底层是如何工作的,无需翻找其在 Python 安装目录中的源码位置,直接在 pdb 中输入:

```text b inspect.currentframe ```

pdb 就会自动在 `inspect.currentframe` 函数入口处设下断点。输入 `c` 继续执行,程序命中该断点,调用栈直接显示从用户代码跳入了 `inspect` 库内部,甚至能清晰看到它底层实际调用了 `sys._getframe()`。当某些断点不再需要时,可使用 `clear <断点编号>` 单独删除(例如 `clear 1` 删除 1 号断点),若直接执行 `clear` 不带编号,则会清空当前所有的断点。

在总结调试器的通用性时,码农高天强调:“不同语言不同程序的 debugger 都大同小异,它们的核心功能基本都是一致的。这里更希望传递给大家是 debugger 的基本理念和一些简单的用法。等大家熟悉了 debug 之后,自然就知道它为什么比 print 要灵活了。”

- 零修改启动:`python -m pdb script.py`,程序在启动入口自动暂停。 - `b <line>` / `break <line>`:在当前文件的指定行号设置断点。 - `b <func>` / `break <module.func>`:直接在函数或模块函数入口打断点,无需关心源码路径。 - `break`(无参数):打印当前所有的断点列表及对应的编号。 - `clear <id>`:删除指定编号的断点;不加编号则清除全部断点。 - `q` / `quit`:立即退出调试并终止整个程序运行。

## Notable quotes > [00:00](https://www.youtube.com/watch?v=oyIJOCSgTM0&t=0s) 任何一个 debugger 无非就是两大块的核心功能:第一是在我感兴趣的地方暂停程序,第二是在暂停程序的时候去检视程序的状态。 — 码农高天

## Mentioned Python, pdb, 码农高天, inspect, sys

## Video description 这期视频讲一下Python自带的debugger pdb的基础用法,以及调试器的基本概念。print虽好,但是也有不灵的时候。

[Source](https://www.youtube.com/watch?v=oyIJOCSgTM0)

中文简报

Python原生pdb实现零侵入断点 终结传统print排错盲区 相比反复插入print语句,pdb无需修改源码即可穿透标准库与第三方依赖,并通过运行时堆栈导航与动态篡改内存状态大幅提升复杂程序的排错效率。 视频系统性地拆解了任何调试器的两大核心本质:在指定位置暂停程序,以及在暂停时检视与干预程序状态。作者首先演示了如何使用内置的 breakpoint() 函数中断程序,并通过 p、w、l、ll 以及调用栈切换命令(u/d)检视上下文。随后,视频深入讲解了流程控制技巧,包括 n(单步跳过)、s(单步进入)、until(跳出循环)与运行时动态修改变量值。最后,作者展示了如何使用 python -m pdb 进行零代码侵入调试,以及如何直接针对函数名(如第三方库或内置库)打断点以深入探查底层源码。 调试器的核心理念可归结为两大功能:在程序运行到关键节点时暂停,并在暂停时全方位检视与干预程序状态,其效率与灵活性远超传统的 print 调试。 (0:00) Python 原生提供 breakpoint() 内置函数与 pdb 交互工具,支持自上而下的调用栈回溯(where/up/down)与源码上下文速览(ll)。 (0:00) pdb 支持 next、step、until 等细粒度流程控制,并允许在调试交互行中直接执行 Python 语句修改内存变量,主动验证排错假设。 (2:37) 通过 python -m pdb 命令行启动或针对函数名设断点(如 b inspect.currentframe),可在完全不侵入源码的前提下直接追踪第三方库或 Python 标准库底层逻辑。 (6:06)

Reading summary

码农高天指出,调试的本质是在特定时刻弄清程序的执行状态;相比传统的 print 输出,掌握 Python 官方内置调试器 pdb 能够更高效地完成暂停定位、运行时检视与控制流调度。 断点暂停与运行时状态检视 (0:00) 在具备源码修改权限时,Python 3.7+ 推荐直接在目标位置插入内置函数 breakpoint(),程序执行到该处会自动中断并进入 pdb 交互命令行。此时界面会清晰展示绝对路径、行号、所处函数以及即将执行的代码。开发者不仅可以使用 p(print)计算并打印变量或表达式,还能通过 w(where)查看从上至下的调用栈层级。 为了更好地掌握执行上下文,pdb 提供了 l(list)查看前后 11 行代码、l. 回到执行行以及 ll(long list)直接展示当前函数全部源码的功能。若需要排查外层调用者的上下文,可以通过 u(up)与 d(down)在不同栈帧间自由切换,从而动态重置当前的局部变量作用域。 控制执行流与动态篡改内存 (2:37) pdb 的核心在于精细控制代码的步进逻辑。使用 n(next)执行单步步过,会将整行代码直接运行完毕;而面对包含函数调用的代码行时,使用 s(step)则会单步步入函数内部探查细节,两者的本质分水岭仅在于当前行是否存在函数调用。当函数即将结束时,可通过特有的 retval 命令打印返回值;在循环或长函数中,则能利用 until 命令(可附加行号)跳出循环,或用 r(return)直接运行至函数返回前夕。 除了单步控制,pdb 还支持在交互提示符下直接执行合法的 Python 语句来修改运行时状态。例如在断点暂停期间,可以直接输入赋值语句篡改内存中的变量内容(如 list = [1, 2, 3]),后续代码便会基于新数据继续执行,便于开发者主动验证程序假设;确认无误后输入 c(continue)即可恢复全速执行。 零侵入式调试与断点管理 (6:06) 面对不可修改的脚本或排查第三方库、标准库底层的场景,开发者可以使用 python -m pdb your_script.py 在入口处自动中断,实现零源码侵入调试。在该模式下,可以通过 b(break)后接行号手动标记断点,再配合 c 命令直接跳跃执行,完全无需修改磁盘文件。 断点设置还支持以函数名作为参数,例如直接输入 b inspect.currentframe,pdb 便能自动锁定该模块函数的入口,配合 c 执行即可直抵库代码内部并结合调用栈追踪底层实现。通过无参数的 break 可查看所有断点列表与编号,clear 则可按编号删除或清空断点,调试完毕后输入 q(quit)即可随时退出程序。 不同语言的调试器在核心理念上高度相通,掌握 pdb 暂停、检视与流转的基本操作,能为排查复杂调用链路和无源码依赖问题提供比 print 更高效、灵活的排错思维。

Source quotes

[0:00] 码农高天: 任何一个 debugger 无非就是两大块的核心功能:第一是在我感兴趣的地方暂停程序,第二是在暂停程序的时候去检视程序的状态。

Original transcript

[0:00] 这期视频带我们来介绍一下PDB的基础用法大家知道 当你的项目做的越复杂的时候 你就要花越多的时间在debugging上很多人debug的方式就是print 对吧print当然是一个非常有效的方法但是在很多情况下如果你能熟练的使用debugger的话会比print效率更高pdb就是python官方提供的命令行的debugger我们所谓的debug 无非就是想知道这个程序在这个时候在干什么因此任何一个debugger无非就是两大块的核心功能第一是在我感兴趣的地方暂停程序第二是在暂停程序的时候去检视程序的状态那我们接下来就一边举例子一边给大家讲解在拍档中如果你对原代码有控制权的话我个人最推荐的方式就是在你感兴趣的地方加一个breakpoint你平时可能是在你的代码里面加一个print对吧现在呢你就把它变成breakpoint这个时候运行的代码你就会发现你进入了pdb的命令行这零病令行前面是一个pdb 代表你已经开始debug了在这个命令行输入的上方有两行信息第一行代表你现在的位置前面的一大串是你文件的绝对路径括号6代表第6行f 代表你在这个f函数里而下面这行代码就是你即将运行的代码我们看在左边缘代码第5行增加了breakpoint之后它就停在了第6行前面即将运行第6行那让程序停下来之后,我们就可以检视程序的状态了最基础的肯定是打印变量对吧这里我们可以用命令P 叫Print Px他就给你打印出来,x是3另外一个常用的命令是查看调用战这个命令是W或者是where这里我们可以看到调用战一共有两层Python叼战的写法是从上往下调用的在这个文件的第12行f3调用了一个函数而现在我们在这个文件的第六行F函数里如果我们想看当前位置附近的原代码 我们可以使用L或者list这里他默认会打印当前行前后的11行代码再输入一个L,它会往下再翻11行L点可以回到当前的行数但是还有一个很长用的命令叫做LL long list它可以显示当前这个函数的全部代码我们在查看调用站的时候,左边会有这么一个小箭头,表示当前所在的frame。当我们打印变量的时候会从当前真的局部变量先涨我们可以通过U或者up命令来改变当前真这个时候再打印X就会发现没有了因为我们现在已经不是在fx这个函数里了而是到了它的上一层然后我们可以通过D或者DOWN往下调整当前针这些就是我们最常用的检视当前程序状态的命令那一个调试器除了能检视当前的状态之外它一定还有能控制程序运行比如说我们现在应该运行第六行了我们可以通过命令N或者Next让它运行一行程序这个时候我们再看 我们就该运行这行forloop了同时我们可以把这个list打印出来了因为list在上一行已经初始化完毕了当然这个时候还没有爱对吧因为我们还没有运行full loop这一行我们可以再往下走一行,这个时候进入了full loop,我们就有I这个变量了。那接下来我们即将运行v等于g这行代码如果我们在这里继续使用n的话,它就会直接把这个函数运行完毕,把well求出来,然后到下一行。有的时候我们的需求是想看看G函数里面发生了什么而不是把这一行直接跳过去这个时候我们需要用到的命令是s也就是step我们还在进行了step命令之后,pdb会告诉我们这里进行了一个函数调用我们现在就在这个G函数里了看一下调用战就变成了三层了那在这行数里面我们想接着往下运行我们可以使用n对吧 跑到了下一行大家注意一下next和step只有在这一行代码里有函数调用的时候才不一样在有函数调用的时候next的意思是你把这个函数直接调用完然后到下一行而step的意思是进入这个函数的调用这里我们再next一下,它会提示你这个函数即将返回我们可以通过writeVal命令拿到这个返回值是0为什么是0呢 因为传进来的data是0我们在 next 一下就到了第九行这个well已经被求出来了打印的内容可以是任何一个合法的python expression比如这里我们打一个tuple while 到号list它就能打印出来well是0 list是空的那我们再往前走一行大家注意这个时候他从第九行跳到了第七行因为这是一个复尔循环嘛他回去了我们再打印这个well list就会发现 list 里面 [5:00] 那我们知道这个循环还没有结束所以如果这块我再用next的话它还会跳回第七行那我怎么告诉他你往下运行直到你运行到第十行为止这个时候我们可以用这个until命令until的意思是运行直到你的行数比我现在的行数大为止这个是一个跳出循环比较好用的手段until命令是可以有一个argument的比如说我们重新运行下这个程序现在我们在第六行我们想整个跳过这个循环我们可以做一个until 10也就是运行到你的行数大于等于10为止这样它就直接跑到第十行了那还有的时候我们在函数里面我们希望运行到它返回为止这个时候我们可以用r或者是return命令它就会停在这个函数返回之前我们可以查看一下返回值是014那pdb的强大之处在于它是可以在debugger里面运行任何的python命令的举个例子我们现在已经初始化了这个list现在这个list是空的对吧我们可以直接跑一个list等于123然后我们跑一整个循环我们就会发现这个list是123后面加了一个0也就是说调试器不仅可以让你检视这个程序的状态也可以让你改变这个程序的状态好假如说你调试完了我想让这个程序正常的运行下去不要接着调试了我们就用这个命令C或者continue就再把这个程序接着跑吧当然这里我写这个程序接着跑就结束了对吧好那有的时候我们对原代码是没有控制力的我们并不能随便修改这个原代码那在这种情况下你的print就不好使了比如说我想debug一下我这个程序是不是在调用库的时候调用的那个库里面出问题了或者不一定是他出问题了我想知道调用的那个库里发生了什么事情是不是里面发生的事情导致了我的程序有bug这种时候print是完全没有办法帮助到你的但是bdb是可以的首先一种显而易见的方法就是我们刚才提到的在你自己的原代码里加入一个breakpoint然后你可以通过step命令进入到第三方酷的原代码里当然还有一种方式我们可以什么原代码都不修改直接跑pdb我们看我们可以使用python-mpdb然后你这个文件来运行这个程序运行之后它会在第一个能停下来的地方就停下来这里就是我们在module里面定义函数的时候那我们这个时候当然可以一行一行往下跑一直跑到我们想要的地方为止但是呢很多时候这个过程太漫长了我知道我想要它在哪停下来那我们就要设置断点也就是所谓的breakpoint在刚才我们是在原代码里面直接加一个breakpoint来人为的在原代码层面设置断点那我们在pdb里也可以通过命令来设置断点比如说我们想在当前文件的第五行设置断点我们只需要打b5b就是breakpoint的意思这里他就会告诉你在这个文件的第五行设置了一个breakpointb命令和break命令是等价的如果你这个break命令后面什么都不加就是列出所有你的breakpoint有了断点之后我们就可以让它往前跑可以看到在continue命令之后它在这个断点的地方停了下来那我们就可以按照刚才的方式去继续debug了这样呢你在完全不修改原代码的情况下也可以调试你的程序那如果你调试完了想直接从debug里退出来可以直接使用queue命令或者quit好我们举一个真正用到酷的例子比如说这里我第9行加了一个frame等于inspect.currentframeinspect是一个built-in的酷对吧有时候可能想知道它里面在干什么我们用刚才的方式运行这个程序当然这里我们可以设一个breakpoint到第9行但是我们还有一个方式我们看我们可以直接避inspect.currentframe它就会直接在current frame这个函数里面帮你设下断点当你在continue的时候它就会在这个函数里面停下来我们看我们的调用站是在我们自己的代码里调用了inspect.current frame然后到了current frame函数里面当然如果你感兴趣的话也可以看一下current frame的代码它实际上调用的是sys如果有一个breakpoint用完了我们想把它删除我们可以用clear1这里的1代表我们刚刚设置断点的编号是1如果你不加这个1的话就是删除所有的breakpointpdb其实是一个还蛮强大的工具今天我们只介绍了一些他非常基本的用法其实不同语言不同程序的debugger都大同小异他们的核心功能基本都是一致的这里更希望传递给大家是debugger的基本理念和一些简单的用法 [10:00] 用法等大家熟悉了debug之后自然就知道他为什么比print要灵活了那个时候你自然会对高级的命令有更多的需求从而去学习这些高级的命令好 这支影片就到这里 喜欢上去记得点下关注 别忘了一键三连哦优优独播剧场——YoYo Television Series Exclusive