开发5分钟,调试2小时 – 该如何debug?

综合编程 2018-09-25 阅读原文

几年来我在答疑群、论坛、公众号、知乎回答的各种问题,没有一万也有八千。其中有三分之二以上都是在帮人看报错,帮人 debug(调试代码)

可以说, 会不会 debug,有没有 debug 的意识,懂不懂 debug 的技巧,是有没有入门编程的重要标志 。然而绝大多数的编程书籍和课程都不会强调这点。教的人和学的人都只关注明面上的知识点,却往往忽略了学习的本质在于“授之以渔”。

在我们的 码上行动基础课程 里,《 如何 debug 》是必须掌握的一个章节。后来发现不少有基础的同学直接参与 爬虫课程 ,但同样缺失 debug 的基本功,于是我又把 debug 作为前序章节加入到爬虫课程里。

为什么很多初学者会觉得编程论坛里的人都不大友好?我经常对提问者说:你这个问题我无法回答,因为你 缺少最基本的调试信息 。而如果你把调试信息提供到位了,基本也就不需要我回答了。归根结底,是从来没有人告诉过初学者, 你得先调试 。大部分人都是碰了很多次壁最后终于明白,啊多么痛的领悟!

今天我就从 debug 课程里挑选最关键的几点讲一下。不要光 mark,现在就看一遍, 嫌长就看加粗高亮部分 。以后开发中再遇到问题,按照以下要点自查。

1. 选一个好的 IDE

初学者一半以上的问题是 低级错误 ,比如 缩进错误(空格、tab混用)、变量前后命名不一致(拼写错误)、函数调用时传递的参数不对、少引号括号、用了中文全角 等等。这些人眼很难一下发现的错误,代码编辑器可以在写代码的时候就给你提示出来。


如图中,分别有一个函数名写错了、一个变量名拼错了,还有一个 if 前面的空格我用了 Tab 键而不是 4 个空格。Pycharm 在你写代码时就会给出高亮或者波浪线的提示,把鼠标移上去还可以看到错误的提示。否则这些错误是很难被发现的。

另外,IDE 的一个重要功能就是可以 自动补全代码 ,省事而且不容易出错。

Python 自带的 IDLE 在这方面做得并不好,刚起步时可以用用,上手后尽快转。 个人推荐用 Pycharm ,即使是功能相对较少的 免费社区版本 ,也足够应付开发时需求。 VSCode 也是很好的选择 之前有过两篇介绍文章, 公众号里回复 pycharm 可查看

2. 学会看报错信息

绝大多数问题不是直接在代码层面就出现的,而是 运行时才发生 。这时候正常都会导致 程序中断并输出报错信息 。一般从报错信息里就可以看出 报错的位置和原因


很多初学者怕看报错信息,这不行。即使英语不好,也尝试着去看,套路就那么些,看几次你就大概熟悉了,但对你调试的帮助非常大。

不过有时报错显示的位置是有问题的,这种情况通常的原因都是前面某一行的括号或者引号有问题,而导致后面的代码错乱。所以 如果报错指出的位置看不出问题,试着往前看一看

在代码中使用 try...except 可以避免程序因异常中断。但在 开发时不要使用异常处理 ,否则阻止了报错信息的发出,影响调试。

报错信息还有个用处就是 直接复制到搜索引擎里进行搜索 ,而这方面 Google 的结果比百度要好,英文网站要比中文网站好。(怎样用 Google 这个话题不便在此公开讨论。)


3. 别舍不得用 print

这一点是最重要的!

在程序中输出并分析 log(日志)是一种很基本但却很灵活很有效的调试方式。使用 print 就是 log 的最常用方法。

log 的作用主要是:

a. 确定程序的运行路径。 一个函数有没有被调用,一个 if 块有没有被执行,一个 while 循环执行了几次,到了哪一步中断了,都可以通过 print 出相关信息来查看。

b. 查看变量的状态。 程序自身的报错会告诉你发生了什么错误,但你还需要找出为什么会发生错误。 通过 print 输出出错语句涉及到的相关变量的值和类型 ,可以帮助分析出错原因。

c. 找出出错位置。 往往错误的原因并不在报错的位置,所以多输出一些标记,多 print 不同位置的变量值, 查看变量在运行过程中值的变化情况 ,可以观察是在哪里发生了问题。

几个通过 log 调试的经验技巧:

a. 多输出一些辅助信息 ,方便自己查看,不然一堆数据看花眼。比如我一般输出时会标注上 变量名,再输出变量类型,以及变量的值 。比如:


b. 在出错行之前输出 。报错行涉及的一些变量,他们的数值和类型,全都输出出来,看看和预期是否一致。

c. 一行做一件事 。如果你出错的一行里连续调用了多个函数或运算,请分开写,分开输出。

d. 对于字符串 ,直接 print 会被转义和解码,影响对变量实际值的观察。可以用 print(repr(text)) 或 print([text]) 的方式查看。

e. 对于编码问题,用好 type 方法和 chardet 库辅助判断 。这点之前编码相关的文章里有说明, 公众号里回复 pycharm 可查看

f. 为了方便记录和回溯问题日志,通常也会 将 log 输出到文件 。也有专门的 logging 模块做这事。

记住: print 不要钱,能用多少是多少! 确保你清楚程序运行的状态细节。

4. 断点调试

断点调试看参考之前文章 如何在 Python 中使用断点调试 ,这里不再复述。

前面这些基本是操作层面的具体 debug 方法,下面我再来谈点 debug 的思想和原则

1. 缩小出错的范围

软件开发中有一个“单元测试”的概念:如果你写了一个函数,应该先运行下这个函数是不是正常,各种参数下会不会出现错误。这样可以 把问题控制在较小的范围内 解决。

2. 控制变量法 + 二分查找法

比如做爬虫没有抓到正确数据,那么到底是 (1)请求来的数值不对,还是 (2)文本处理方法不对?如果你认为是文本处理方法没有问题,就手动给定一个正确格式的字符串文本,应该可以得到正确的结果。这就相当于 控制了因素(2),只改变因素(1),根据执行结果就可以确认或排除问题的所在

二分查找法是一个定位问题的技巧。如果你的代码里有问题,那么问题要么在前半段,要么在后半段。你先把后半段代码去掉执行,看看程序是否还报错,就知道错误在哪部分。依次类推, 不断折半,直到找到引发报错的代码

3. 先重现,再解决,最后验证

有的错误不是每次出现。这时候最好别急着去改代码,而是 想办法可以稳定重现问题 。当你能顺利重现问题的时候,通常离分析解决它也不远了。解决了之后, 再按照之前重现的方式验证修改 是否确实有效。

4. 想清楚再动手

不论是开发代码,还是调试 debug,在做一个动作前要清楚自己的目的是什么,而不是盲目地进行改动。 不要猜! 我经常会看见一些新手遇到问题之后,反复执行代码,或者不断调整参数,妄想某次执行程序就能神奇地通过了。这是一个很不好的态度,请避免。 要恪守逻辑 ,知道现在要解决的问题是什么,需要得到那些信息,可能的假设是什么,如何通过修改代码去验证你的判断,这样才是合理的 debug 方式。

以上就是一些 debug 的基本思想和技巧。这里仅仅是概要精华,操作起来远不止这么多,同时还需要实际的练习才能掌握。我能做的就是给你的编程技能树下埋一颗种子,能不能生根发芽,就看各位自己的浇灌了。(我相信,即使我这里都说过了,还是会有很多人依旧犯上述提及的错误 ╮(╯_╰)╭ 随缘吧,师傅领进门,修行在个人……)

想要深入了解更多编程的思想和技巧,欢迎来我们的 码上行动知识星球

下课!

════

其他文章及回答:

如何自学Python |新手引导 |精选Python 问答 |Python单词表 |知乎下载器 |人工智能 |嘻哈 |爬虫 |我用Python |高考 |requests |AI平台

欢迎搜索及关注: Crossin的编程教室

责编内容by:Crossin的编程教室 【阅读原文】。感谢您的支持!

您可能感兴趣的

An Introduction to Python Classes “Sometimes it’s worth lingering on the journey for a while before getting to the destination.” ― Richelle Mead, The Indi...
Hack the Box Challenge: Jail Walkthrough Hello friends!! Today we are going to solve another CTF challenge “Jail” which is available online for those who wan...
rstripping Simon Pegg: Don’t use rstrip for file e... In Python, what does '/path/to/my/simon_pegg.jpeg'.rstrip('.jpeg') yield? If you guessed '/path/to/m...
机器学习 Python 环境搭建 之前零零散散的介绍了部分工具的环境搭建。今天打算整合一下,从头到尾开始搭建机器学习过程中需要用到的工具及其安装方法。后续如果用到其他工具也会持续更新到这篇文章。 Python 虚拟环境安装 这里使用 pyenv 管理不同的 pyth...
Introducing Spyder, the Scientific PYthon Developm... If you want to use Anaconda for science projects, one of the first things to consider is the spyder package, which is in...