异常信息与定位
学习层次
读懂 Traceback、识别异常类型和定位出错行属于核心掌握。它们是后续标准库练习和独立排错的前置能力。
程序出错时,Python 会给出一份 Traceback。面对一大段报错信息,如何判断错误类型、找到出错代码,并确定下一步检查什么?本章先建立统一的阅读顺序,再用常见异常练习定位方法。
不需要背下所有异常名称,重点是读出报错提供的线索,并知道常见异常应该先检查什么。
异常定义
程序执行到无法继续处理的状态时,Python 如何说明原因?先从最小示例认识异常对象和 Traceback 的基本信息。
程序执行过程中遇到无法继续处理的问题时,会抛出异常。如果异常没有被处理,程序就会停止,并输出 Traceback。
例如,除数为 0 时会抛出 ZeroDivisionError:
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero最后一行包含两部分:
ZeroDivisionError:异常类型。division by zero:错误说明。
异常类型帮助我们判断问题属于哪一类,错误说明则会补充这一次出错的具体原因。
Traceback 结构
Traceback 可以理解为程序出错前留下的调用记录。排查时按下面的顺序读取:
- 看最后一行,确认异常类型和错误说明。
- 从下往上看调用位置,先找最接近异常的代码。
- 根据文件名和行号回到自己的代码。
- 检查这一行用到的变量和前面的执行过程。
单层调用定位
最简单的报错只有一个代码位置:
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero可以这样读:
- 最后一行说明执行除法时除数为
0。 - 上一行说明问题出现在当前输入的第 1 行。
- 回到
1 / 0,检查除数为什么会得到0。
多层调用定位
程序经过多层函数调用后,Traceback 会列出完整的调用过程:
def calculate_average(total, count):
return total / count
def show_average(total, count):
print(calculate_average(total, count))
show_average(100, 0)运行结果类似下面这样:
Traceback (most recent call last):
File "main.py", line 9, in <module>
show_average(100, 0)
File "main.py", line 6, in show_average
print(calculate_average(total, count))
File "main.py", line 2, in calculate_average
return total / count
ZeroDivisionError: division by zero这段信息说明:
- 程序从
show_average(100, 0)开始调用。 show_average()又调用了calculate_average()。- 真正抛出异常的是
return total / count。 - 此时传入的
count是0。
排查多层调用时,不要只停在最外层的调用代码。先找到最接近异常的位置,再沿调用过程检查错误数据从哪里传进来。
常见异常类型
当最后一行给出异常类型后,下一步应该检查什么?下面按基础练习中常见的异常类型,结合最小示例整理排查方向。
NameError
访问了一个没有定义的名称:
>>> student_name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'student_name' is not defined优先检查变量是否已经定义,以及名称有没有写错。
TypeError
当前数据类型不支持这次操作:
>>> "年龄:" + 18
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str优先检查参与运算的数据类型,以及函数收到的参数类型。
ValueError
数据类型可以接收,但具体的值不符合要求:
>>> int("abc")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'abc'int() 可以接收字符串,但 "abc" 不是有效的整数内容。
ZeroDivisionError
执行除法时,除数的结果为 0:
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero看到这个异常,要继续检查除数为什么会变成 0,不能只检查代码里有没有直接写出数字 0。
IndexError
序列索引超出范围:
>>> scores = [90, 85, 92]
>>> scores[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range列表只有 3 个元素,有效索引是 0、1、2。看到这个异常,优先检查序列长度和索引值。
KeyError
访问了字典中不存在的键:
>>> server = {"host": "127.0.0.1", "port": 80}
>>> server["name"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'name'优先检查键名有没有写错,以及这个键是否一定存在。键可能不存在时,可以根据需求使用 get()。
FileNotFoundError
要读取的文件不存在:
>>> open("students.json", encoding="utf-8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'students.json'优先检查文件名、文件路径和程序当前的工作目录。
AttributeError
访问了对象不存在的属性或方法:
>>> name = "Python"
>>> name.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'age'优先检查属性名,以及当前变量保存的究竟是哪一种对象。
SyntaxError
代码不符合 Python 语法:
>>> if score >= 60
File "<stdin>", line 1
if score >= 60
^
SyntaxError: expected ':'SyntaxError 通常发生在程序正式执行前。箭头会尽量标出解释器发现问题的位置,但真正缺失或多余的内容也可能在它附近。
常见原因包括:
- 括号或引号没有配对。
if、for、while、def等语句末尾缺少冒号。- 关键字或表达式没有写完整。
异常定位流程
再次遇到异常时,可以按这套流程把报错线索落实到具体代码和运行数据:
- 重新运行一次,确认问题能够稳定出现。
- 保留完整 Traceback,不要只截取最后一句。
- 从最后一行确认异常类型和错误说明。
- 根据文件名和行号找到自己的代码。
- 检查这一行用到的数据,以及数据从哪里传来。
- 修改后再次运行,确认原来的操作已经恢复正常。
常见异常可以提供排查方向,但异常名称不会直接告诉你最终修改方案。例如 ZeroDivisionError 说明除数为 0,至于应该修改数据、调整执行顺序,还是增加边界处理,还要结合程序需求判断。
小结
本节重点掌握三件事:
- Traceback 包含异常类型、错误说明、文件位置和调用过程。
- 多层调用出错时,先从最接近异常的位置开始检查。
- 异常类型提供排查方向,最终原因仍要回到代码和运行数据中确认。
下一节继续解决:哪些异常应该处理,以及怎样用 try...except 让程序作出合适的响应。
练习
- 分别制造一个
NameError、TypeError和IndexError,观察最后一行有什么不同。 - 把
show_average(100, 0)改成可以正常运行的调用,再比较两次结果。 - 找一段以前出现过的完整 Traceback,按“异常类型、错误说明、出错位置、调用过程”重新读一遍。