我见过太多人卡在一个Bug上三小时,对着屏幕发呆,反复打印变量。这种死磕效率极低。我自己踩过无数坑,总结了一套方法,能把你定位问题的速度提上去。先说清楚,这套方法不是万能的,但能覆盖日常工作中九成的报错。
第一步 看懂报错信息
报错信息最后一行才是线索。很多人从上面开始看,读完一堆调用栈就慌了。其实你只需要最后一行,它会告诉你是什么类型的错误,比如 KeyError、TypeError、IndexError。知道类型之后,去翻文档或靠经验就能快速缩小范围。举个例子,KeyError 八成是字典里没有这个键,检查一下数据来源就行。
调用栈中间那几行不用全看,只看最近一个项目的代码文件就行。标准库和第三方包的调用栈可以跳过,它们很少出问题。出问题的地方基本是你自己写的逻辑。
第二步 写最小的测试用例
不要直接在业务代码里乱改,先造一个最小化的复现场景。把有问题的数据单独抽出来,写个脚本来跑。比如你遇到一个列表越界,那就在控制台里直接建一个同样结构的数据,手动调那个函数。一步就能看到结果。这比跑到项目里跑几百行代码快得多。
最小化测试还有一个好处:能帮你确认问题到底是出在数据上还是逻辑上。很多时候你以为的逻辑错误,其实是上游传进来的数据格式变了。你单独用安全数据测一遍,马上就能判断。
第三步 分段注释法
把函数体注释掉一半,跑一下看还报不报错。如果还报错,说明问题在你注释掉的部分之前。如果不报了,问题就在那半段里。重复这个过程,二分法,几轮下来就能锁定一行代码。这个方法看似笨,但对付复杂逻辑非常管用,尤其当你不知道代码的依赖关系时。
注意,注释的时候别把函数调用链断掉,你可以把那段逻辑替换成一个返回固定值的临时写法。保证程序能运行到错误位置就行。
第四步 打印关键变量的值
别用 print('调试1') 这种标签,要打就打出真正有意义的东西。比如把某个字典的长度、某个列表的第一个元素、某个对象的类型都打印出来。很多时候Bug是类型错误,比如你以为传进来的是字符串,其实是。加上 type() 和 len() 就能直接发现。
打印的时候最好带上前缀,比如 print('请求数据长度:', len(data))。这样在输出里一眼就能找到。别嫌多,宁可多打几个也不漏掉关键线索。
第五步 用IDE断点代替print
如果你还在用print接print,赶紧换成断点调试。在代码行号旁边点一下,让那行停下来。然后逐行执行,看变量变化。很多诡异的问题——比如循环里变量被意外覆盖、条件判断分支不对——用断点一走就明明白白。主流IDE都有这个功能,把鼠标悬停在变量上就能看到值,比print读起来顺手得多。
断点调试不挑语言,Python里也一样用。你只需要记住按F10是单步执行,F11是进入函数内部。
第六步 查文档别查百度
遇到库函数用法问题,优先去Python官方文档或该库的GitHub仓库看。百度搜索结果里很多是复制粘贴的口水帖,有的还是错的。官方文档写得很清楚,函数的参数类型、返回值、异常场景都有说明。花五分钟翻一下,比看十篇博客有效。
如果是第三方库,去它的GitHub页面搜issue。你遇到的Bug之前大概率有人遇到。看别人怎么解决的,比自己重新造轮子快。比如Django报错时搜“Django [错误码] site:github.com”,马上就能找到相关讨论。
第七步 调用链追踪
记下函数被谁调用的,以及传入参数是什么。有时候Bug不是你写的函数有问题,而是调用方传错了参数。你拿到的是空列表,调用方给的就是空列表。这种情况调一万遍也没用。在函数开始处打印一下 locals(),把当前作用域内所有变量都打出来,一比较就知道哪里坏了。
如果项目用了日志框架,把日志级别调到DEBUG模式,很多框架自带的日志里就包含了调用参数和耗时数据。看那些日志比你自己加输出快得多。
第八步 遇到诡异Bug别怀疑是机器中毒
九成九的诡异Bug都是你自己的代码有问题。比如变量名拼写错误,漏了一个下划线;或者索引写反了把数组读成了元组;又或者是环境变量里某个路径漏了斜杠。先冷静下来,把代码从头到尾读一遍。很多时候读着读着就发现问题了。别急着重装系统或换电脑,那是浪费人生。
如果是并发相关的Bug,多打印一些时间戳和线程ID,找一找数据竞争的条件。这种问题最难复现,但一旦找到规律,修复起来其实很简单。
第九步 写代码前先想清楚
最好的排错方法是不让Bug出现。写代码时先想清楚输入是什么,输出是什么,边界条件怎么处理。多用assert在开头检查参数合法性。提前把防御逻辑写好,后续调试能省一半力气。比如接收用户输入时,先判断是不是None,是不是空字符串,是不是还没转类型。这些检查写在前头,后面逻辑就不会因为类型错误而崩。
另外,代码分段要有注释,哪怕只是“这段是校验参数”、“这段是处理数据”。能让自己后来快速跳进跳出。
第十步 善用git二分查找
如果功能之前能跑,现在不跑了,用git bisect找出是哪次提交引入的Bug。这个命令自动帮你二分搜索历史提交。你只需要告诉它哪个版本正常,哪个版本异常。它自动切版本让你测试,一直找到罪魁祸首。比手动翻commit快得多。
记住,不要一次修复很多问题。每次只改一个变量、一行逻辑,然后立刻测试。这样你知道改的是什么。批量改后出错了,你都不知道是哪个改动导致的。
以上就是我每天实际在用的排查方法。没有花哨的技巧,只有重复操作的积累。遇到Bug别慌,一步一步按套路走,时间会省下一半以上。你试过之后会发现,很多问题根本就没有那么复杂。