了解一些方法助你降低代码查错时间。
--MariaMckinley
在周六的上午三点钟(为何是这个时间?由于事情总会在周日晚上三点钟发生),你收到一条通知,顾客发觉你的软件出现一个错误。在有了初步的怀疑后,你联系运维,查看你的软件日志以了解发生了哪些,由于你记得收到过日志早已搬家了的通知。
结果那些日志被转移到了你获取不到的地方,但它们正在导出到一个网页应用中——所以待会儿你可以用这个漂亮的应拿来检索日志,并且,这个应用现今还没完成。这个应用预计会在几天内完成。我晓得,你认为这完全不切实际。但是并不是,日志或则日志消息虽然时常在错误的时间消失不见。在我们开始查错前,一个忠告:常常检测你的日志以确保它们还在你觉得它们应当在的地方,并记录你觉得它们应当记的东西。当你不注意的时侯,这种东西常常会发生令人惊叹的变化。
好的,你找到了日志或则尝试了呼叫运维人员,而顾客确实发觉了一个错误。甚至你可能觉得你已然晓得错误在哪里。
你立刻打开你觉得可能有问题的文件并开始查错。
1、先不要碰你的代码
阅读代码,你甚至可能会想到该阅读什么部份。并且在开始搞乱你的代码前,请再现造成错误的调用并把它弄成一个测试。这将是一个集成测试,由于你可能还有其他疑惑,目前你还不能确切地晓得问题在哪里。
确保这个测试结果是失败的。这很重要,由于有时你的测试不能再现失败的调用,尤其是你使用了可以混淆测试的web或则其他框架。好多东西可能被储存在变量中linux桌面,但遗憾的是,只通过观察测试,你在测试里调用的东西并不总是显著可见的。当我尝试注重现这个失败的调用时,我并不是说我要创建一个可以通过的测试,并且,好吧,我确实是创建了一个测试,但我不觉得这非常不寻常。
从自己的错误中汲取教训。
2、编写错误的测试
如今,你有了一个失败的测试,或则可能是一个带有错误的测试,这么是时侯解决问题了。并且在你开干之前,让我们先检测下调用栈,由于这样可以更轻松地解决问题。
调用栈包括你已然启动但仍未完成地所有任务。为此,例如你正在烤面包并打算往馅料里加面条,那你的调用栈将是:
你早已开始做面包,开始打馅料,而你如今正在加面条。往锅底抹油不在这个列表中,由于你已然完成了,而做糖霜不在这个列表上由于你还没开始做。
假如你对调用栈不清楚,我强烈建议你使用PythonTutor,它能帮你在执行代码时观察调用栈。
如今,假如你的Python程序出现了错误,Python类库会帮你复印出当前调用栈。这意味着无论那一时刻程序在做哪些,很显著错误发生在调用栈的顶部。
3、始终先检测调用栈顶部
在栈底你除了能看见发生了那个错误,但是一般可以在调用栈的最后一行发觉问题。假如栈底对你没有帮助,而你的代码还没有经过代码剖析linux命令ls,这么使用代码剖析是十分有用的。我推荐pylint或则flake8。一般情况下,它会强调我仍然忽视的错误的地方。
倘若错误看上去很蒙蔽,你下一步行动可能是用Google搜索它。假如你搜索的内容不包含你的代码的相关信息,如变量名、文件等,那你将获得更好的搜索结果。假如你使用的是Python3(你应当使用它),这么搜索内容包含Python3是有帮助的,否则Python2的解决方案常常会抢占大多数。
许久曾经,开发者须要在没有搜索引擎的帮助下解决问题。那是一段黑暗岁月。充分借助你可以使用的所有工具。
不幸的是,有时侯问题发生在更早阶段,但只有在调用栈顶部执行的地方才显露下来。如同当面包没有膨胀时,忘掉加发酵粉的事才被发觉。
那就该检测整个调用栈。问题更可能在你的代码而不是Python标准库或则第三方包,所以先检测调用栈内你的代码。另外,在你的代码中放置断点一般会更容易检测代码。在调用栈的代码中放置断点,之后瞧瞧周围是否如你预期。
“但是,玛丽,”我看见你说,“如果我有一个调用栈,那这种都是有帮助的,但我只有一个失败的测试。我该从那里开始?”
pdb,一个Python调试器。
找到你代码里会被这个调用命中的地方。你应当才能找到起码一个这样的地方。在哪里打上一个pdb的断点。
一句正题
为何不使用print句子呢?我以前依赖于print句子。有时侯,它们依然很便捷。但当我开始处理复杂的代码库linux内核打印调用栈,尤其是有网路调用的代码库,print句子就显得太慢了。我最终在各类地方都加上了print句子,但我无法追踪它们的位置和缘由,但是显得更复杂了。并且主要使用pdb还有一个更重要的诱因。假定你添加一条print句子去发觉错误问题,但是print句子必须早于错误出现的地方。并且,瞧瞧你放print句子的函数,你不晓得你的代码是如何执行到哪个位置的。查看代码是找寻调用路径的好方式,但看你曾经写的代码是惊悚的。是的,我会用grep处理我的代码库以找寻调用函数的地方,但这会显得平庸,并且搜索一个通用函数时并不能缩小搜索范围。pdb就显得十分有用。
你遵守我的建议,打上pdb断点并运行你的测试。但是测试再度失败,并且没有任何一个断点被命中。留着你的断点,并运行测试套件中一个同这个失败的测试十分相像的测试。假如你有个不错的测试套件,你应当才能找到一个这样的测试。它会命中了你觉得你的失败测试应当命中的代码。运行这个测试,之后当它运行到你的断点,按下w并检测调用栈。假如你不晓得怎么查看由于其他调用而显得混乱的调用栈,这么在调用栈的中间找到属于你的代码,并在堆栈中该代码的上一行放置一个断点。再试一次新的测试。假如一直没命中断点,这么继续,向下追踪调用栈并找出你的调用在那里脱轨了。假如你仍然没有命中断点,最后到了追踪的底部,这么恭喜你,你发觉了问题:你的应用程序名称拼写错了。
没有经验,小白,一点都没有经验。
4、修改代码
假如你仍认为蒙蔽,在你稍为改变了一些的地方尝试新的测试。你能让新的测试跑上去么?有哪些是不同的呢?有哪些是相同的呢?尝试改变一下别的东西。当你有了你的测试,以及可能也还有其它的测试,那就可以开始安全地更改代码了,确定是否可以缩小问题范围。记得从一个新递交开始解决问题,以易于可以轻松地撤消无效地修改。(这就是版本控制,假如你没有使用过版本控制,这将会改变你的生活。好吧,可能它只是让编码更容易。查阅“版本控制可视手册”,以了解更多。)
5、休息一下
虽然这么linux内核打印调用栈,当它不再觉得上去像一个有趣的挑战或则游戏而开始显得令人失望时,你最好的措施是脱离这个问题。休息一下。我强烈建议你去遛弯并尝试考虑别的事情。
6、把一切写出来
当你回去了,若果你没有忽然遭到启发,那就把你关于这个问题所知的每一个点信息写出来。这应当包括:
有时这儿有好多信息,但相信我,从零碎中挖掘信息是很碍眼。所以尽量简约,而且要完整。
7、寻求帮助
我时常发觉写下所有信息才能启迪我想到还没尝试过的东西。其实,有时侯我在点击求援电邮(或表单)的递交按键后立即意识到问题是是哪些。无论怎样,当你在写下所有东西仍一无所获时,那就试试向别人发短信求援。首先是你的朋友或则其他参与你的项目的人,之后是该项目的电邮列表。不要害怕向人求救。大多数人都是友善和乐于助人的,我发觉在Python社区里尤其这么。
MariaMcKinley已在PyCascades2019讲演代码查错,2月23-24,于西雅图。
via:
作者:MariaMckinley选题:lujun9972译者:LazyWolfLin校对:wxy
本文由LCTT原创编译,Linux中国荣誉推出