
第七章 断点调试:VSCode 调试全流程(含常见坑)
-
- [0. 先给你一个结论:调试效率 = 结构 + 日志 + 断点](#0. 先给你一个结论:调试效率 = 结构 + 日志 + 断点)
- [1. 准备工作:让 VSCode "调得起来"](#1. 准备工作:让 VSCode “调得起来”)
-
- [1.1 必须满足的 3 个前提(不然会踩坑)](#1.1 必须满足的 3 个前提(不然会踩坑))
- [2. VSCode 调试核心:launch.json 只要写对一次](#2. VSCode 调试核心:launch.json 只要写对一次)
- [3. 第一种调试:调脚本(最常用,也是最推荐的工程入口)](#3. 第一种调试:调脚本(最常用,也是最推荐的工程入口))
-
- [3.1 launch.json:调 scripts/run_etl.py](#3.1 launch.json:调 scripts/run_etl.py)
- [4. 第二种调试:调模块(强烈推荐,最稳定)](#4. 第二种调试:调模块(强烈推荐,最稳定))
-
- [4.1 launch.json:python -m 模式](#4.1 launch.json:python -m 模式)
- [5. 第三种调试:调 pytest(定位回归 bug 的核武器)](#5. 第三种调试:调 pytest(定位回归 bug 的核武器))
-
- [5.1 launch.json:调 pytest 单测](#5.1 launch.json:调 pytest 单测)
- [6. 断点调试的"黄金动作":你必须掌握的 6 个操作](#6. 断点调试的“黄金动作”:你必须掌握的 6 个操作)
-
- [6.1 打断点(Breakpoint)](#6.1 打断点(Breakpoint))
- [6.2 Step Over(F10)](#6.2 Step Over(F10))
- [6.3 Step Into(F11)](#6.3 Step Into(F11))
- [6.4 Step Out(Shift+F11)](#6.4 Step Out(Shift+F11))
- [6.5 Watch(监视变量)](#6.5 Watch(监视变量))
- [6.6 Call Stack(调用栈)](#6.6 Call Stack(调用栈))
- [7. 条件断点:数据项目最强技巧(没有之一)](#7. 条件断点:数据项目最强技巧(没有之一))
-
- [7.1 条件断点示例](#7.1 条件断点示例)
- [8. 数据分析项目的断点位置清单(直接照抄)](#8. 数据分析项目的断点位置清单(直接照抄))
-
- [8.1 数据读入后(第一断点)](#8.1 数据读入后(第一断点))
- [8.2 清洗前后(第二断点)](#8.2 清洗前后(第二断点))
- [8.3 特征工程输出(第三断点)](#8.3 特征工程输出(第三断点))
- [8.4 模型训练前后(第四断点)](#8.4 模型训练前后(第四断点))
- [8.5 导出前(第五断点)](#8.5 导出前(第五断点))
- [9. 常见坑(含解决方案):你踩一次就会明白](#9. 常见坑(含解决方案):你踩一次就会明白)
-
- [坑 1:VSCode 调试用的不是你的 .venv](#坑 1:VSCode 调试用的不是你的 .venv)
- [坑 2:src/ 导入失败(ModuleNotFoundError)](#坑 2:src/ 导入失败(ModuleNotFoundError))
- [坑 3:断点不生效(灰色空心点)](#坑 3:断点不生效(灰色空心点))
- [坑 4:Notebook 调试很痛苦](#坑 4:Notebook 调试很痛苦)
- [坑 5:多进程/多线程调试不稳定](#坑 5:多进程/多线程调试不稳定)
- [10. VSCode 调试与 logging 的最佳配合方式](#10. VSCode 调试与 logging 的最佳配合方式)
- [11. 本章最低交付(MDR):你必须交付什么?](#11. 本章最低交付(MDR):你必须交付什么?)
- [12. 小结:断点调试是你工程能力跃迁的分水岭](#12. 小结:断点调试是你工程能力跃迁的分水岭)
(从"盲猜 + print"升级到"现场勘验 + 证据链排错")
如果你已经把前两章(目录结构、配置管理、日志体系)做起来,你会明显感觉:项目开始"像工程"了。
但真正让你排错效率再上一个台阶的,是这一章:断点调试。
很多同学对调试的理解停留在:
- 报错了 → 搜索错误信息
- 不知道哪里错 → 插
print() - 终于找到问题 → 删 print(或忘了删)
而在真实交付里,我们不会靠猜。我们靠"进入现场":
- 断点:让程序停在关键节点
- 变量现场:查看数据结构、内容、类型、shape
- 调用栈:还原错误的路径
- 逐步执行:确认哪一行开始变坏
- 条件断点:只在特定样本/特定条件下停住
这一章我会按"工程交付"的方式,给你一套 VSCode 调试全流程模板:
你照着做,就能把"排错"从体力活变成流程化能力。
0. 先给你一个结论:调试效率 = 结构 + 日志 + 断点
你如果只用断点,不做结构化目录和 logging,调试依然会痛苦。
正确路径应该是:
目录结构稳定
配置可控
日志可追溯
断点调试进入现场
定位更快 + 复现更稳 + 修复更准
所以我建议你把这一章当作"工程排错能力"的最后一块拼图。
1. 准备工作:让 VSCode "调得起来"
1.1 必须满足的 3 个前提(不然会踩坑)
- 项目使用虚拟环境
.venv(上一章已做) - 项目采用
src/布局(第四章已做) - 入口明确:你知道你要调的是哪一个入口(scripts/CLI/pytest/Notebook)
大多数调试失败,其实不是 VSCode 不行,而是"入口不清、环境没对"。
2. VSCode 调试核心:launch.json 只要写对一次
在 VSCode 里,调试最终都会落到 .vscode/launch.json。
你写对一次,后面所有项目都能复用。
建议你项目里新建:
text
.vscode/
launch.json
3. 第一种调试:调脚本(最常用,也是最推荐的工程入口)
假设你的入口脚本是:
scripts/run_etl.py
或者scripts/run_train.py
那么你只需要一个最基础的配置:
3.1 launch.json:调 scripts/run_etl.py
json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug: run_etl",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/scripts/run_etl.py",
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"APP_ENV": "dev"
}
}
]
}
你只要记住四个关键点:
program指向入口脚本console用 integratedTerminal(方便看输出)env用于注入环境变量(与第五章配置管理配合)justMyCode: true(避免跳进第三方库,调试更清爽)
4. 第二种调试:调模块(强烈推荐,最稳定)
很多工程项目的入口不是脚本,而是模块:
bash
python -m myproj.cli --help
这种方式更稳定(尤其 src/ 布局)。VSCode 也支持:
4.1 launch.json:python -m 模式
json
{
"name": "Debug: module myproj.cli",
"type": "python",
"request": "launch",
"module": "myproj.cli",
"args": ["--help"],
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"APP_ENV": "dev"
}
}
经验结论:
如果你项目采用 src/ 布局,调试模块往往比调脚本更少出"导入玄学"。
5. 第三种调试:调 pytest(定位回归 bug 的核武器)
当你开始写 tests 后,最划算的调试方式其实是:直接调 failing test。
5.1 launch.json:调 pytest 单测
json
{
"name": "Debug: pytest (single file)",
"type": "python",
"request": "launch",
"module": "pytest",
"args": ["-q", "tests/test_etl_clean.py::test_clean_keeps_required_columns"],
"console": "integratedTerminal",
"justMyCode": true
}
这种方式的优势在于:
- 输入数据可控
- 复现路径固定
- 一次定位一个 bug
- 修完即可防回归
对"可维护项目"来说,这就是最好的排错闭环。
6. 断点调试的"黄金动作":你必须掌握的 6 个操作
很多人会打断点,但不会用"调试动作"。我建议你把下面 6 个动作当作必修。
6.1 打断点(Breakpoint)
- 行号左侧点击即可
- 习惯在"输入边界""状态改变""输出前"打断点
6.2 Step Over(F10)
- 单步执行当前行
- 不进入函数内部
适合看流程是否按预期走
6.3 Step Into(F11)
- 进入函数内部
适合定位"哪个函数把数据改坏了"
6.4 Step Out(Shift+F11)
- 从当前函数跳出
适合你发现进入太深,想回到上层调用
6.5 Watch(监视变量)
把你关心的变量表达式加入 watch,例如:
df.shapedf.columnscfg.raw["data"]["input_path"]len(records)
6.6 Call Stack(调用栈)
这是你理解 bug 的关键。
如果你看到报错在某一行,但不知道谁调用了它,看 call stack,一眼就明白。
入口 run_etl.py
load_config
load_data
clean_df
KeyError 发生点
断点调试就是把这条链路"可视化"。
7. 条件断点:数据项目最强技巧(没有之一)
数据工程的 bug 很多是"某一行数据导致的"。
你不可能每次都逐步跑到那一行。
这时条件断点就是神器。
7.1 条件断点示例
假设你在循环处理记录:
python
for i, row in enumerate(rows):
...
你只想在 i == 1000 时停住:
右键断点 → Add Conditional Breakpoint → i == 1000
或者只在某列缺失时停:
row["title"] is Nonepd.isna(row["date"])
这会让你从"盲跑"变成"精准抓捕"。
8. 数据分析项目的断点位置清单(直接照抄)
很多同学问:断点到底打哪?
我给你一份最实用的"点位清单"。
8.1 数据读入后(第一断点)
看三件事:
df.shapedf.columnsdf.head()
8.2 清洗前后(第二断点)
看四件事:
- 缺失率变化
- 重复行变化
- 异常值处理数量
- 关键列是否仍存在
8.3 特征工程输出(第三断点)
- 特征维度是否一致
- 是否存在全 NaN / 全 0 列
- train/test 切分是否泄漏
8.4 模型训练前后(第四断点)
- 输入 X/y 形状
- 超参是否正确加载
- 指标输出是否符合期望
8.5 导出前(第五断点)
- 输出路径
- artifacts 是否生成
- runs 目录是否完整
你会发现:按这个点位打断点,80% 的 bug 都能快速定位。
9. 常见坑(含解决方案):你踩一次就会明白
这一节是"交付级经验"。你很可能在调试时遇到这些坑。
坑 1:VSCode 调试用的不是你的 .venv
现象: 你明明装了库,调试时提示找不到;或者版本不对。
解决:
- VSCode 右下角选择 Python Interpreter → 指向
.venv - 或在 launch.json 增加(可选)
"python": "${workspaceFolder}/.venv/bin/python"
坑 2:src/ 导入失败(ModuleNotFoundError)
现象: 调试脚本时导入 myproj 报错
原因: 你的运行入口不在正确工作目录,或未以模块方式运行
解决:
- 推荐改用
"module": "myproj.cli" - 或保证
program入口在 workspace 下,且已pip install -e .
坑 3:断点不生效(灰色空心点)
原因常见于:
- 代码与运行的不是同一份文件(你改了但没保存)
- 你在第三方库或非调试路径里
justMyCode配置影响进入第三方
解决:
- 保存文件
- 确认运行入口
- 必要时把
justMyCode临时设为 false
坑 4:Notebook 调试很痛苦
结论:Notebook 更适合探索,不适合作为工程入口。
建议: 把核心逻辑下沉 src,用脚本/模块/pytest 调试。
坑 5:多进程/多线程调试不稳定
如果你用了并行(joblib、multiprocessing),调试难度会增加。
建议: 排错阶段先关并行,把并行作为性能优化后置。
10. VSCode 调试与 logging 的最佳配合方式
断点调试负责"现场勘验",日志负责"证据链回放"。
两者配合,排错效率直接拉满:
- 先用日志定位到"哪个阶段坏了"
- 再用断点进入"具体哪一行坏了"
- 修复后用 pytest 回归验证
- 最后输出 runs 证据链(log + config_snapshot + metrics)
log定位阶段
断点定位行
修复代码
pytest回归
runs留证
这就是工程化排错闭环。
11. 本章最低交付(MDR):你必须交付什么?
按照专栏学习协议,这章完成后你至少交付:
.vscode/launch.json(至少包含 2 个配置:脚本入口 + pytest 或模块入口)- 能在 VSCode 中打断点并停住(截图或说明写进 README)
- 至少使用一次 Watch + Call Stack 定位问题(写进 notes)
- 准备 1 个"条件断点"示例(数据循环场景必备)
- README 增加 Debug 小节:别人如何复现你的调试入口
你做到这些,你的排错能力就从"手工插 print"升级为"工程调试"。
12. 小结:断点调试是你工程能力跃迁的分水岭
你会发现:当你习惯用断点调试后,很多问题不再"玄学"。
- 你不再猜
- 你进入现场
- 你看数据结构与调用栈
- 你用条件断点精准抓 bug
- 你用 pytest 锁住修复成果
下一章我们会进入测试体系(pytest),把"修 bug"升级为"防 bug",让你的项目具备长期可维护性。
《第八章 单元测试入门:pytest 让代码"可长期维护"》
如果你愿意,你可以把你现在最常遇到的一个报错(完整 traceback + 出错文件路径)贴出来,我会按本章流程给你一个"断点点位建议":应该在什么地方打断点、看哪些变量、用什么条件断点最快定位。