调试:自己找出问题
一份实用的调试指南,教你如何在不求助他人的情况下隔离、验证并真正理解你的 bug。
大多数初学者把 bug 当作一堵墙。他们撞上去,停下来,然后让别人帮他们翻过去。真正能培养技能的习惯是不同的:你学会把 bug 当作一个有答案的问题,在去提问之前自己先找出答案。
把错误信息当成证据来读,而不是噪音
Stack traces 经常被跳过,因为它们看起来很吓人。但它们通常能告诉你问题在哪里以及为什么出现了。如果你在 Python 中得到 TypeError: 'NoneType' object is not subscriptable,那不是随机的乱码——这意味着你期望包含列表或字典的变量实际上是 None。行号告诉你在哪里。你的工作是从那一行向后追踪,找到值应该被设置但没有被设置的地方。
在网上搜索任何东西之前先做这个:先读 traceback 的最后一行,然后往上读。最后一行通常命名实际的异常。上面的行显示了把你带到这里的调用链。
有目的地重现它
如果一个 bug 只是有时出现,你还没理解它。在改动代码之前,试着让它可靠地发生。每次改变一个输入。它在空列表上失败但在满列表上不失败吗?它只在函数的第二次调用上失败,而不是第一次吗?一个你能按需重现的 bug 是 80% 已解决的 bug,因为现在你可以测试一个修复是否真的有效,而不是猜测。
把问题对半切,然后再对半切
二分查找不仅适用于排序数组——它是在一个长函数或管道中找到坏代码的最快方法。注释掉或绕过你逻辑的后半部分,检查前半部分是否仍然产生 bug。如果是,问题在前半部分。如果不是,就在后半部分。重复。这对 200 行脚本同样有效,对多阶段数据管道也同样有效,你可以在每个阶段用 print() 或 df.head() 检查输出。
这比在压力下随机改变行然后重新运行要好得多,那样做会浪费更多时间。
当 print() 停止工作时使用调试器而不是 print()
print() 语句对简单情况很好,但一旦你在多个函数调用间追踪状态,实际的调试器能节省真正的时间。在 Python 中,在可疑的一行之前放 import pdb; pdb.set_trace(),运行脚本,你就得到一个实时提示符,你可以在其中检查变量,用 n 逐行执行,用 s 进入函数调用。VS Code 的内置调试器用你点击而不是输入的断点做同样的事情。无论哪种方式,目标都是一样的:观察你的程序的实际状态,而不是猜测它可能是什么。
把它从你的其他项目中隔离出来
如果一个 bug 发生在一个大代码库内,不要在大代码库内调试它。把相关函数复制到一个新文件中,用能触发相同失败的假的、最小的输入。如果 bug 消失了,周围的某些东西——一个全局变量、一个 import、陈旧的状态——才是真正的原因,你刚刚学到了一些重要的东西。如果 bug 在隔离中仍然存在,你现在有一个小的、可共享的、可测试的用例,你已经更接近实际的修复了。
根据现实检查你的假设,而不是记忆
大部分调试时间都花在未经质疑的假设上:
本文由人工智能协助撰写,经 Michal Pilch(CISSP)审核并发布,Korra Studio。
这是来自 Korra Studio 知识库的笔记之一——该平台将每个主题与一对一指导相结合。
免费开始arrow_forward