Python调试基础:调试的概念、断点调试、单步执行、查看变量

调试,说白了就是找bug的过程。你写了代码,它没按你预期跑,或者干脆报错。这时候你总不能盯着屏幕干瞪眼吧?你需要一套方法,让代码一步步告诉你,“我到底卡在哪了”。

我刚开始用Python时,调试全靠print。满屏都是print(“到这里了”)、print(a)。后来发现,这办法在小脚本里还行,一旦项目大了,print就跟蜘蛛网似的,删都删不干净。嗯,咱们今天聊的,是更专业的手段——用调试器。

调试的概念:你其实每天都在做

调试就是“观察程序执行状态,定位并修复错误”。听起来挺学术的,其实就是几个动作:

  • 让程序停在某个怀疑的位置
  • 检查那一瞬间所有变量的值
  • 一步一步往前走,看状态怎么变的
  • 找到哪里跟预期不一样,然后改代码

我曾经接手过一个项目,对接第三方API,传参总出错。肉眼看了三遍,参数名一个没拼错。后来用调试器一看,发现是某个中间函数偷偷把参数类型从int转成了str。那个错误要是靠print找,估计得印出一座喜马拉雅山。

核心思维:调试不是写代码的反面,而是写代码的一部分。你花在调试上的时间,往往是理解代码深度最好的时间。

断点调试:让程序在你想停的地方停

断点,就是你在代码行号旁边点一下,程序跑到这里会自动暂停。它不是报错,是“报到”。

我个人习惯是这样操作的:

  1. 在怀疑出错的那一行左侧点击,出现红色圆点
  2. 启动调试模式(不是直接运行)
  3. 程序会在红点处暂停,等待你发号施令

来看看一个简单例子。假设你有这么一段代码:

def calculate_price(base, discount):
    result = base * discount
    return result

total = calculate_price(100, 0.8)
print(total)

你在result = base * discount这一行设了断点。程序启动后,会停在这一行,但result还没被算出来。这时候你可以检查base和discount的值——它们应该分别是100和0.8。如果发现discount是0.08,那错误就找到了。

断点可以设多个。你可以同时怀疑三个地方,让程序依次停。调试器允许你从第一个断点继续跑到第二个断点,中间不暂停。

单步执行:像看电影一样看代码

断点只是暂停。单步执行才是真正的“慢动作回放”。常见的操作有这几种:

操作 快捷键(VS Code) 作用
继续 F5 跑到下一个断点或结束
单步跳过 F10 执行当前行,不进入函数内部
单步进入 F11 执行当前行,如果遇到函数就跳进去
单步跳出 Shift+F11 从当前函数跳出,回到调用处
重启 Ctrl+Shift+F5 从头开始调试

你想一想,F10和F11有什么区别?我举个例子:

def add(a, b):
    return a + b

x = 10
y = 20
z = add(x, y)
print(z)

如果断点设在了z = add(x, y)这一行,按F10,调试器会直接执行add(x, y),然后停在print(z)那一行。你只知道结果,看不到add内部发生了什么。但按F11,调试器会跳到add函数的内部,让你逐行看看a和b是怎么相加的。

注意:如果你在循环里按F11,而循环里调用了函数,你会被带进函数里很多次。这时候可以先设个条件断点——右键断点,设置“当i等于100时再停”。不然你按F11按到手指抽筋。

查看变量:调试的核心目标

调试器暂停时,你真正想看的是变量。在VS Code里,左边会有一个“变量”面板,自动列出当前作用域的所有变量。你还可以把鼠标悬停在代码中的变量上,直接看到它的值。如果你在Python自带的pdb里,可以输入:

(Pdb) print(a)
(Pdb) type(a)
(Pdb) dir(a)

为什么要看变量类型?我曾经被这个问题坑过:

data = {"name": "张三", "age": "25"}
if data["age"] > 18:
    print("成年")

你看出来了吗?age是个字符串“25”,不是整数25。"25" > 18在Python 3里会直接报错,因为字符串和整数不能比较。如果你用调试器一查,type(data["age"])显示<class 'str'>,问题一秒定位。

更常用的操作是在“监视”窗口添加表达式。比如你加一个len(data),调试器每次暂停都会计算这个值。这对检查大批量数据非常有用。

实战技巧:调试批量对接参数时,我习惯把每个请求的payload打印到“监视”窗口,然后用“继续”功能快速跑几十次,观察参数有没有规律性错误。比我用print一个一个看,快了不止十倍。

一个完整的调试流程 SVG

下面这张图,展示了一次典型调试的完整步骤。从怀疑bug,到定位问题,再到修复验证,每一步都清晰可见。

典型调试流程 怀疑代码有Bug 设置断点 启动调试模式 程序停在断点处 查看变量值/类型 单步跳过/进入 发现值与预期不符 定位错误根源 修改代码并保存 重新调试验证修复 确认Bug已修复 循环直到所有Bug都修复

很多新手调试会卡在“不知道看什么”。我建议你每次停在断点,先看三样东西:

  1. 当前行:这是哪一步?上下文对不对?
  2. 局部变量:值是不是你传进来的?有没有被意外修改?
  3. 调用栈:程序是怎么跑到这里来的?有没有绕了远路?

看清楚了这三样,八成的问题你已经能猜个大概了。剩下两成,再单步细看。

在VS Code里,调用栈在左侧的“调用堆栈”面板。你点击栈里的任意一行,就能看到那行代码的变量。这个功能在排查“谁把我传进来的参数改了”时特别好用。

总结:调试三板斧

咱们这一章讲的,其实就是三件事:

  • 断点:在哪停
  • 单步:怎么走
  • 变量:看什么

这三件事做好了,百分之九十的bug都可以原地消灭。下一节开始写代码练习前,我建议你先在随便一个Python脚本里按几下F11,感受一下“代码像电影一样逐帧播放”的感觉。上手之后你会发现,调试器才是你最好的代码审查工具,比任何静态检查都管用。

嗯,反正我现在写代码,调试器基本没关过。