读懂报错与调试
4 道练习题·预计 60 分钟·做对一题解锁下一段
前言
实战 2 写出了记账本 v1,前面又学了函数,咱们写的代码是越来越长了。
代码一长,碰到报错的机会就多了。不知道各位童鞋是不是这样:程序一报错,屏幕上冒出一大段英文,心里先慌了一半。然后要么把整段复制去搜,要么干脆从头再敲一遍,祈祷这次别出错。
其实,报错信息一点都不可怕。它就像医生开的诊断书,出了什么毛病、毛病在哪一行,都写得清清楚楚,就差直接告诉你怎么改了。可惜很多人从来没有认真读过它。
这一章,咱们不学新语法,专门练一件事:读懂报错,自己把 bug 找出来。
这件事没有捷径,只能多看、多练。一开始读得慢没关系,读多了你会发现,常见的报错翻来覆去就那么几种,看一眼最后一行,心里就有数了。
学这一章之前,需要先学完 实战 2 的记账本 v1,以及 第 6 章 的函数:怎么定义函数、怎么传参数、怎么用 return 返回结果。
目录
这一章会学到:
- 一、记账本崩了——输错一个金额,程序就停了
- 二、报错信息从下往上读——错在哪一行,又是怎么走到这一行的
- 三、最常见的几种报错——看最后一行,就知道是什么毛病
- 四、输错了,别崩——先检查,再转换
- 五、不报错的 bug,怎么找?——把变量打印出来看
- 六、常见坑
- 七、自己查一次文档——遇到没见过的报错怎么办
- 八、记账本 v1.1
- 九、小结与回看
学完这一章,你能读懂报错信息,自己找到 bug 在哪儿。记账本也会升级到 v1.1,金额输错了不会再崩,而是客客气气地请你重新输入。
一、记账本崩了
实战 2 的结尾说过,记账本有个大毛病:金额一输错,程序就崩。先来看看,它崩的时候是什么样子:
item = input('买了什么:')
cost = float(input('花了多少钱:'))
print(f'记下了:{item},{cost:.2f} 元')金额那里,输入的是「十二块」。在网页里运行的话,程序不会打印「记下了」,而是给出这样一段:
买了什么:奶茶
花了多少钱:十二块
Traceback (most recent call last):
File "main.py", line 2, in <module>
cost = float(input('花了多少钱:'))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: '十二块'是不是有点头大?一堆英文,又是 File 又是 line 的。
先别慌。这一段其实只说了三件事:
- 出了什么错:看最后一行。
ValueError,没法把字符串'十二块'转成浮点数。 - 错在哪儿:看
File "main.py", line 2这一行和它下面贴出来的代码。main.py是网页给你的代码起的文件名,出错的是第 2 行,也就是cost = float(input('花了多少钱:'))。下面那一串^,指着出错的那一段。 - 是怎么走到这儿的:
Traceback下面那几行,记录了程序是一层一层怎么调用过来的。这个例子很简单,只有一层。
剩下的,都是细节。不过,要是程序里用了函数,报错信息还会再长一截。下一节,咱们就换一个带函数的例子,一行一行地把它拆开。
二、报错信息从下往上读
动手拆之前,先认识一下它的名字。报错信息有个正式的名字,叫 traceback,直译过来是「回溯」,意思是把程序出错之前走过的路,一步一步倒着列出来。
下面就拿一个带函数的例子来看。把记一笔账的步骤包成函数 add_entry() ,第二次调用的时候,金额写成了中文的「二十五」:
def add_entry(entries, item, text):
cost = float(text)
entries.append({'item': item, 'cost': cost})
entries = []
add_entry(entries, '奶茶', '12.5')
add_entry(entries, '午饭', '二十五')
print(entries)把它存成 ledger.py ,在自己电脑上运行,看到的是这样的(文件路径每台电脑都不一样,这里用 ... 代替):
Traceback (most recent call last):
File ".../ledger.py", line 8, in <module>
add_entry(entries, '午饭', '二十五')
File ".../ledger.py", line 2, in add_entry
cost = float(text)
^^^^^^^^^^^
ValueError: could not convert string to float: '二十五'第一行 Traceback (most recent call last) 的意思是「最近的一次调用在最后」。所以读的时候,要从下往上读:
- 最后一行:错误类型和说明。
ValueError是「值不对」,后面说没法把'二十五'转成浮点数。 - 往上一层:
line 2, in add_entry,错误发生在add_entry函数里,第 2 行。下面还贴出了这一行代码cost = float(text)。Python 3.11 及以上的版本,还会在出错的地方下面画一串^。 - 再往上一层:
line 8, in <module>,是第 8 行调用了add_entry(),才走到那里的。<module>指的是最外层的代码,不在任何函数里面。
连起来读就是:第 8 行调用 add_entry() → 进到函数里的第 2 行 → float('二十五') 出错了。
这么一读,问题也就清楚了:函数本身没毛病,是第 8 行传进去的金额不对。
那在网页里运行同一段代码,看到的又是什么样子呢?
Traceback (most recent call last):
File "main.py", line 8, in <module>
add_entry(entries, '午饭', '二十五')
File "main.py", line 2, in add_entry
cost = float(text)
^^^^^^^^^^^
ValueError: could not convert string to float: '二十五'对比一下就会发现,它和自己电脑上的几乎一模一样,只有文件名不同。因为网页里的代码不是存在你电脑上的文件,所以统一叫 main.py ,前面也没有文件夹路径。看到 line 8 ,就去编辑器里数到第 8 行。
不管在哪儿运行,读法都一样:先看最后一行是什么错,再往上找你自己代码里的行号。
这一章里会报错的示例代码,页面上没有放运行按钮。想亲眼看看报错长什么样,可以把代码复制到练习题的编辑器里运行,也可以存成文件,在自己电脑上运行。下面的第一道题,就是一段一运行就报错的代码。
先去做这道题,照着报错把 bug 修好。做完了,下一节再来认一认初学时最常碰到的几种报错。
编辑器里的代码有两个 bug,一运行就报错。
先运行,读报错信息:最后一行是什么错?往上找,是你代码里的第几行、在哪个函数里?一次只改一处,改完马上再运行。
两处都改好后,运行的输出应该是:
一共 2 笔,合计 37.50 元推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai