除错与测试:找出程式的病
进阶错误三类:语法(格式错、无法启动)、运行时(跑到一半停,如除以零)、逻辑(跑完但结果错);用已知答案的测试资料验证,用 print 和追踪表找变数问题。
- 错误三类:语法、运行时、逻辑。
- 语法错误启动不了;运行时错误跑到一半停;逻辑错误跑完但结果错。
- 错误讯息最后一行讲类型,上面讲行号 — 先读讯息再动手。
文体小知识:方法指导课教你一套有系统的做法,照步骤练习,遇到问题就不会毫无头绪。
§课文原文 Passage
1程式出错是家常便饭,找出并修正错误的过程叫除错(Debugging)。错误分三大类:语法错误(Syntax error)— 写错格式,例如漏了冒号,程式一行都跑不到;运行时错误(Runtime error)— 执行到一半才停下,例如除以零或存取不存在的索引;逻辑错误(Logic error)— 程式顺利跑完,但结果不对,例如把加写成乘,最难察觉。
2Python 会直接告诉你语法和运行时错误:SyntaxError: invalid syntax、ZeroDivisionError: division by zero。看懂错误讯息的最后一行(错误类型)和上面的行号(出事位置),问题已经解决了一半。读错误讯息是每个程式员的基本功。
3逻辑错误电脑不会报错,要靠自己测试(Testing):用已知道答案的资料试跑,例如计平均分的程式,输入 [10, 20, 30] 应该得出 20;如果得出 6000(不小心乘了),就知道出了问题。这些「先知答案、再验证」的资料叫测试资料(Test data),还要包括边界情况:空清单、全 0、只有一个数。
4逐步追踪(Trace)是基本功:拿一张纸,一行一行写下每个变数的变化,例如 n=1 → 印 1 → n=2 → 印 2 → n=3 → 印 3 → n=4 条件不成立结束;也可以在程式中间加 print(n) 观察变数。养成「小改一步、测一次」的习惯,错误就不会积少成多。
1重点摘要 Summary
错误三类:语法(格式错、无法启动)、运行时(跑到一半停,如除以零)、逻辑(跑完但结果错);用已知答案的测试资料验证,用 print 和追踪表找变数问题。
2生字卡 Vocabulary
程式中的错误或缺陷,令程式不能运行或结果不对。
找出错误位置和原因,并加以修正的过程。
程式格式不符合语言规则(如漏冒号),令程式完全无法启动。
程式执行到一半才发生的错误,例如除以零、索引超出范围。
程式顺利执行完毕,但因演算法写错而得出错误结果,最难发现。
Python 显示的错误报告:最后一行是错误类型,上面有出事的行号。
答案已知的输入资料,用来验证程式是否正确,包括边界情况。
逐行记录变数数值变化的表格,用来找出逻辑出错的位置。
输入范围极端的情况:最小值、最大值、0、空清单、只有一个元素。
3逐步讲解 Steps
- 辨认语法错误语法错误是程式的「文法」写错了,例如 if x > 5 漏了结尾的冒号,或者 print('hi' 漏了右括号,Python 都会报 SyntaxError。特征非常明显:程式无法执行任何一行,一启动便报错,错误讯息会指出出事的行号。初学者最常犯的是漏冒号、括号不配对,以及中英文标点混用,例如输入了全形冒号。见到 SyntaxError,先检查该行和上一行的符号是否齐全。
- 辨认运行时错误运行时错误是程式执行到一半才发生的错误,例如 10 / 0 会报 ZeroDivisionError;清单 [1, 2] 只有两个元素,却取第 6 个位置的元素(索引 5),便会报 IndexError,因为索引 5 并不存在。特征是:程式能够启动,执行到某一句时突然停止,错误讯息会指出停在哪一行。处理方法:按行号找到该句,检查除数是否可能为零、索引是否超出范围、档案是否存在等。
- 辨认逻辑错误逻辑错误最危险:程式正常跑完,没有任何报错,但答案不对。例如想计平均数,却写成 sum(nums) * len(nums),输入 [10, 20, 30] 时输出 1800,而不是正确的 20。电脑不会提醒你,唯一办法是用已知道答案的资料测试。常见的逻辑错误还包括把加写成乘、把 >= 写成 >、循环次数差一。养成测试习惯,逻辑错误便无所遁形。
- 读懂错误讯息Python 的错误讯息是最有用的线索。例如萤幕出现 ZeroDivisionError: division by zero,还附有一个行号:最后一行讲错误类型,上面的行号指出哪一行出事。阅读次序是先看最后一行知道错误类型,再按行号找到位置,检查那句程式码。看懂错误讯息是每个程式员的基本功,遇到不明白的错误名称,可以先搜寻它的意思,再对照自己的程式码逐步修正。
- 用测试资料验证测试资料是「先知道答案、再验证程式」的资料。以计平均数的程式为例:用 [10, 20, 30] 测试,心算答案 20;用 [5] 测试,只有一个数,答案 5;用 [0, 0] 测试,答案 0。正常值、单一值、边界值全部都要试,因为不同情况触发的错误不同,例如只有一个数时,某些写法便会出错。考试常要求列出测试资料和解释原因,答题时每组资料都要写明预期输出。
- 纸笔追踪表追踪表是找出逻辑错误的基本功。拿一张纸画表格,一行一行记录每个变数的变化。例如 n = 1,程式是 while n < 4: print(n),然后 n = n + 1:第一圈 n 是 1,印出 1;第二圈 n 是 2,印出 2;第三圈 n 是 3,印出 3;第四圈 n 是 4,条件不成立,结束。变数由哪一圈开始与预期不符,表格一目了然,错误的位置便无所遁形。
- print 除错法结果不对时,可以在关键位置加入 print 观察中间值,例如 print('总分:', total),看看总分究竟是多少,从而判断问题出在前半还是后半。找到问题之后,把这些临时 print 删除,保持程式整洁。最重要的原则是:小改一步、测一次,不要一次改五处,否则根本无法知道哪一个修改有效。遵守这个纪律,除错便会事半功倍。
4图解 Diagrams
5例题示范 Example
例题:以下程式想计算 [8, 9, 10] 的平均,但输出 27。找出错误类型并修正。程式码:total = 0;for s in [8, 9, 10]: total = total + s;average = total * 3;print(average)。
- 先分类:程式能够跑完,全程没有报错,所以排除语法错误和运行时错误,剩下的是逻辑错误。
- 用测试资料心算验证:8 + 9 + 10 = 27,总分正确;但题目要的是平均,27 其实是总分而非平均。
- 逐行检查,找到问题行:average = total * 3 把乘号用错了位置,应该是 average = total / 3。
- 修正后测试:27 除以 3 等于 9.0,正确;再用 [10, 20, 30] 验证一次,输出 20,通过测试。
- 答案:这是逻辑错误:程式没有报错但结果不对。把 average = total * 3 改为 average = total / 3,输出变成 9.0;再用 [10, 20, 30] 测试得 20,两组测试资料都通过,修正完成。
6常见错误 Mistakes
75 题快测 Quick check
Q1print("hello(漏了右括号)属于哪类错误?
Q2执行 10 / 0 会发生什么事?
Q3程式顺利跑完,但平均分计错。这是哪类错误?为什么最难发现?
Q4想验证计平均的程式,输入 [10, 20, 30] 时应该期望什么结果?
Q5写出除错的三个好方法(任何三个)。
8重点回顾 Recap
- 错误三类:语法、运行时、逻辑。
- 语法错误启动不了;运行时错误跑到一半停;逻辑错误跑完但结果错。
- 错误讯息最后一行讲类型,上面讲行号 — 先读讯息再动手。
- 用已知答案的测试资料验证,边界情况也要试。
- 除错习惯:小改一步、测一次;善用 print 和追踪表。