Python 环境加载中…

读懂报错与调试

4 道练习题·预计 60 分钟·做对一题解锁下一段

完整通读本章 →

教学 01 / 10· 已读

前言

实战 2 写出了记账本 v1,前面又学了函数,咱们写的代码是越来越长了。

代码一长,碰到报错的机会就多了。不知道各位童鞋是不是这样:程序一报错,屏幕上冒出一大段英文,心里先慌了一半。然后要么把整段复制去搜,要么干脆从头再敲一遍,祈祷这次别出错。

其实,报错信息一点都不可怕。它就像医生开的诊断书,出了什么毛病、毛病在哪一行,都写得清清楚楚,就差直接告诉你怎么改了。可惜很多人从来没有认真读过它。

这一章,咱们不学新语法,专门练一件事:读懂报错,自己把 bug 找出来。

这件事没有捷径,只能多看、多练。一开始读得慢没关系,读多了你会发现,常见的报错翻来覆去就那么几种,看一眼最后一行,心里就有数了。

学这一章之前,需要先学完 实战 2 的记账本 v1,以及 第 6 章 的函数:怎么定义函数、怎么传参数、怎么用 return 返回结果。

目录

这一章会学到:

  • 一、记账本崩了——输错一个金额,程序就停了
  • 二、报错信息从下往上读——错在哪一行,又是怎么走到这一行的
  • 三、最常见的几种报错——看最后一行,就知道是什么毛病
  • 四、输错了,别崩——先检查,再转换
  • 五、不报错的 bug,怎么找?——把变量打印出来看
  • 六、常见坑
  • 七、自己查一次文档——遇到没见过的报错怎么办
  • 八、记账本 v1.1
  • 九、小结与回看

学完这一章,你能读懂报错信息,自己找到 bug 在哪儿。记账本也会升级到 v1.1,金额输错了不会再崩,而是客客气气地请你重新输入。

教学 02 / 10· 已读

一、记账本崩了

实战 2 的结尾说过,记账本有个大毛病:金额一输错,程序就崩。先来看看,它崩的时候是什么样子:

Python· 预期报错示例
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 下面那几行,记录了程序是一层一层怎么调用过来的。这个例子很简单,只有一层。

剩下的,都是细节。不过,要是程序里用了函数,报错信息还会再长一截。下一节,咱们就换一个带函数的例子,一行一行地把它拆开。

教学 03 / 10

二、报错信息从下往上读

动手拆之前,先认识一下它的名字。报错信息有个正式的名字,叫 traceback,直译过来是「回溯」,意思是把程序出错之前走过的路,一步一步倒着列出来。

下面就拿一个带函数的例子来看。把记一笔账的步骤包成函数 add_entry() ,第二次调用的时候,金额写成了中文的「二十五」:

Python· 预期报错示例
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) 的意思是「最近的一次调用在最后」。所以读的时候,要从下往上读:

  1. 最后一行:错误类型和说明。ValueError 是「值不对」,后面说没法把 '二十五' 转成浮点数。
  2. 往上一层:line 2, in add_entry ,错误发生在 add_entry 函数里,第 2 行。下面还贴出了这一行代码 cost = float(text) 。Python 3.11 及以上的版本,还会在出错的地方下面画一串 ^ 。
  3. 再往上一层: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 修好。做完了,下一节再来认一认初学时最常碰到的几种报错。

练习 1 / 4照着报错修 bug题目有问题?

编辑器里的代码有两个 bug,一运行就报错。

先运行,读报错信息:最后一行是什么错?往上找,是你代码里的第几行、在哪个函数里?一次只改一处,改完马上再运行。

两处都改好后,运行的输出应该是:

文本
一共 2 笔,合计 37.50 元
main.py
可编辑
做对当前题解锁下一段 ·04

推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai

学习目录