第一章 调试入门:从“找茬”到“治病”

调试这件事,说白了就是给代码看病。我做了十几年软件,见过太多人把调试当成“碰运气”——改一行代码跑一下,不行再改回来。嗯,这其实不是调试,是瞎蒙。

今天咱们聊聊调试的本质,它跟测试到底有啥区别,以及最常用的几把“手术刀”怎么用。

1.1 调试的本质:定位与修复

调试是什么?我的理解很简单:找到代码里“想的不对”和“写的不对”之间的差距。

你想想看,写代码时脑子里有个预期——这个函数应该返回42。结果跑出来是0。调试就是去追查:为什么是0?哪一步出了问题?

我个人习惯把调试分成三步:

  • 复现:让bug稳定出现。不能复现的bug,基本没法修。
  • 定位:缩小范围,找到罪魁祸首是哪一行、哪个变量。
  • 修复:改代码,并确认没引入新问题。

核心观点:调试不是“修代码”,而是“理解代码”。你理解了,修复自然就来了。

我在项目中遇到过最典型的例子:一个支付模块偶尔多扣用户一分钱。测试跑了100次都没问题,但线上就是有用户投诉。后来我花了两天时间,在日志里加了一堆标记,最终发现是浮点数精度问题——0.01 + 0.02 不等于 0.03,这个坑大家应该都踩过。

1.2 调试与测试的区别

很多人分不清调试和测试,觉得都是“找bug”。其实差别挺大的。

维度 测试 调试
目标 发现bug是否存在 找到bug的原因并修复
执行者 测试人员或自动化脚本 开发人员
工具 单元测试框架、CI系统 调试器、日志、分析工具
输出 测试报告(通过/失败) 代码修改(修复补丁)
时机 开发中、发布前 测试失败后、线上出问题时

举个例子:测试告诉你“登录按钮点了没反应”。这是测试的结论。调试要回答的是“为什么没反应?是事件没绑定?还是接口返回了500?还是前端没处理响应?”

我曾经接手过一个项目,测试报告写了200页,全是“功能A失败”、“功能B异常”。但开发组看了三个月,一个bug都没修好。为什么?因为测试只告诉了你“哪里疼”,没告诉你“为什么疼”。调试才是那个做诊断的医生。

我的建议:写测试时,顺便想想“如果这个测试失败了,我该怎么调试?”提前埋好日志、加好断言,能省很多事。

1.3 常见调试工具概览

工具不在多,够用就行。我平时主要用这三样:print、pdb、IDE调试器。它们各有各的适用场景。

1.3.1 print:最原始,也最直接

别看不起print。我到现在写新代码,第一反应还是加print。它简单、零学习成本、在任何环境都能跑。

def calculate_price(amount, discount):
    print(f"amount={amount}, discount={discount}")  # 看输入
    result = amount * (1 - discount)
    print(f"result={result}")  # 看输出
    return result

但print有个大问题:容易污染代码。调试完了忘了删,或者删不干净,上线后控制台刷一堆垃圾信息。我见过有人线上代码里留着几百行print,把磁盘日志都打爆了。

避坑指南:我曾经在生产环境忘记删一个print,结果打印了用户手机号,差点被合规部门约谈。所以我现在都用日志库(logging)代替print,可以控制级别,上线自动关闭debug输出。

1.3.2 pdb:Python自带的调试器

当print不够用的时候,就该上pdb了。它可以让你在代码任意位置暂停,查看变量、单步执行。

用法很简单:

import pdb

def divide(a, b):
    pdb.set_trace()  # 程序会停在这里
    return a / b

divide(10, 0)

运行后你会进入交互模式,常用命令:

  • n:执行下一行
  • c:继续执行到下一个断点
  • p 变量名:打印变量值
  • l:查看当前上下文代码
  • q:退出调试

我个人习惯在复杂循环里用pdb。比如处理一个100万行的文件,第99999行报错了。用print得改代码重新跑,用pdb可以直接在报错处停下来,看当时的变量状态。

1.3.3 IDE调试器:图形化的“透视眼”

如果你在用VS Code、PyCharm这类IDE,调试器是最高效的工具。它把pdb的功能做成了可视化界面。

核心操作就几个:

  • 断点:在行号旁边点一下,程序跑到这里就停。
  • 单步跳过:一行一行执行,不进入函数内部。
  • 单步进入:进入函数内部,看它怎么算的。
  • 变量面板:所有变量值一目了然,不用自己print。
  • 调用栈:看当前是哪个函数调了哪个函数,一路回溯。

你想想看,一个函数嵌套了5层,用print你得在每个函数里加一行,跑完还得删。用IDE调试器,直接在入口打断点,然后“单步进入”一层层往下看,效率高太多了。

小技巧:条件断点非常实用。比如循环1000次,只在第500次时停下来。右键断点,设置条件为 i == 500 就行。我曾经用这个技巧,十分钟找到了一个只在特定数据量下才触发的bug。

1.4 三种工具怎么选?

我一般这样选:

  • 简单逻辑、快速验证:用print,几秒钟搞定。
  • 复杂流程、需要交互:用pdb,轻量且强大。
  • 大型项目、多层调用:用IDE调试器,可视化效率最高。

没有哪个工具是万能的。我见过有人只用print,遇到多线程bug就抓瞎。也见过有人只会用IDE调试器,上了服务器没有图形界面就完全不会了。所以我的建议是:三个都学会,根据场景切换。

一句话总结:调试的本质是理解代码的运行过程。工具只是帮你“看到”这个过程的手段。别纠结用哪个,先动手调起来再说。

下一章咱们聊聊日志系统——怎么打日志才能既帮自己调试,又不影响线上性能。嗯,这个坑我也踩过不少,到时候细说。