理解 Python 的异常信息是调试代码的关键,当你写的代码出现错误时,Python 会“抛出”(raise)一个异常,如果没有被捕获,程序就会中断并打印出详细的错误信息,学会阅读这些信息,你就能快速定位问题所在。

一个典型的异常信息结构
我们来看一个最常见的例子:ZeroDivisionError。
# 一个会引发异常的代码
def divide(a, b):
return a / b
# 调用函数,传入除数为0
result = divide(10, 0)
print("计算结果:", result)
运行这段代码,你会看到类似下面的输出:
Traceback (most recent call last):
File "example.py", line 8, in <module>
result = divide(10, 0)
File "example.py", line 5, in divide
return a / b
ZeroDivisionError: division by zero
这个异常报告分为几个关键部分,我们逐一解析:
Traceback (most recent call last) - 追踪信息
这是异常报告的标题,告诉你这是一个“回溯”或“追踪”信息,它记录了异常发生时,程序执行过的所有函数调用路径,从你运行的脚本开始,一直到发生错误的那一行代码。

文件名和行号 (File "example.py", line 8, in <module>)
这是追踪信息中的每一帧,告诉你错误发生的具体位置。
File "example.py": 发生错误的文件名。line 8: 发生错误的行号。in <module>: 发生错误时,程序正在执行的代码所在的函数或作用域。<module>表示这是在主程序模块(也就是你直接运行的脚本)中,而不是在某个函数内部。
如果错误发生在函数内部,你会看到更深的调用栈:
File "example.py", line 8, in <module>
result = divide(10, 0)
File "example.py", line 5, in divide <-- 错误发生的函数
return a / b
这清晰地告诉我们:<module> 中的第 8 行调用了 divide 函数,然后在 divide 函数的第 5 行发生了错误。
异常类型 (ZeroDivisionError)
这是错误的核心类型,Python 中有很多内置的异常类型,每种类型代表一种特定类型的错误。

NameError: 尝试访问一个未定义的变量。TypeError: 对类型不兼容的值进行操作(将字符串和数字相加)。ValueError: 传入的值类型正确,但内容不合适(int('abc'))。IndexError: 访问序列中不存在的索引。KeyError: 访问字典中不存在的键。FileNotFoundError: 尝试打开一个不存在的文件。
了解这些异常类型,可以帮助你快速判断问题的性质。
异常消息 (division by zero)
这是对异常的简短描述,解释了为什么会发生这个错误。ZeroDivisionError 的消息就是 "division by zero",非常直观。
如何处理异常:try...except 语句
当预见到某段代码可能会出错时,你可以使用 try...except 来“捕获”异常,防止程序崩溃,并让程序以更优雅的方式处理错误。
def safe_divide(a, b):
try:
# 尝试执行可能出错的代码
result = a / b
except ZeroDivisionError:
# 如果捕获到 ZeroDivisionError,就执行这里的代码
print("错误:不能除以零!")
return None # 返回一个特殊的值表示失败
else:
# try 块中的代码没有出错,就执行这里的代码
return result
finally:
# 无论是否发生异常,finally 块中的代码都会被执行
print("除法运算结束。")
# 测试
print(safe_divide(10, 2)) # 正常情况
print(safe_divide(10, 0)) # 出现异常的情况
输出:
除法运算结束。
5.0
错误:不能除以零!
除法运算结束。
None
try...except 的结构解析:
try: 将可能引发异常的代码块放在try语句下。except: 指定要捕获的异常类型,你可以捕获一个特定的异常(如except ZeroDivisionError),也可以捕获所有异常(不推荐,使用except Exception或except:)。else: 与try语句搭配使用,表示如果try块没有引发异常,则执行else块中的代码。finally: 无论try块是否引发异常,finally块中的代码总会被执行,通常用于清理资源,如关闭文件、释放网络连接等。
如何主动引发异常:raise 语句
除了 Python 自动抛出的异常,你还可以使用 raise 语句在特定条件下主动抛出异常。
def set_age(age):
if not isinstance(age, int):
# 如果传入的参数不是整数,就主动抛出一个 TypeError
raise TypeError("年龄必须是整数")
if age < 0 or age > 120:
# 如果年龄不在合理范围内,就主动抛出一个 ValueError
raise ValueError("年龄必须在 0 到 120 之间")
print(f"设置的年龄是: {age}")
# 正常调用
set_age(25)
# 会引发 TypeError 的调用
try:
set_age("twenty-five")
except TypeError as e:
print(f"捕获到错误: {e}")
# 会引发 ValueError 的调用
try:
set_age(150)
except ValueError as e:
print(f"捕获到错误: {e}")
输出:
设置的年龄是: 25
捕获到错误: 年龄必须是整数
捕获到错误: 年龄必须在 0 到 120 之间
使用 raise 可以让你的函数更健壮,并在出现不符合预期的情况时,强制调用者去处理问题。
获取异常的详细信息
在 except 块中,你可以将异常对象赋给一个变量,从而获取更多关于异常的信息。
try:
# 尝试打开一个不存在的文件
with open("non_existent_file.txt", "r") as f:
content = f.read()
except FileNotFoundError as e:
# e 就是捕获到的异常对象
print(f"捕获到异常: {type(e).__name__}") # 打印异常类型
print(f"异常消息: {e}") # 打印异常消息
print(f"文件名: {e.filename}") # 访问异常对象的特定属性
输出:
捕获到异常: FileNotFoundError
异常消息: non_existent_file.txt
文件名: non_existent_file.txt
很多内置的异常对象都包含有用的属性,e.filename(文件名)、e.args(异常参数元组)等。
自定义异常
你可以通过继承 Python 内置的 Exception 类来创建自己的异常类型,从而使你的代码错误处理逻辑更清晰、更具体。
class InvalidPasswordError(Exception):
"""自定义异常,用于密码不符合要求的情况。"""
def __init__(self, password, message="密码太短,至少需要8个字符"):
self.password = password
self.message = message
super().__init__(self.message)
def check_password(password):
if len(password) < 8:
# 抛出自定义的异常
raise InvalidPasswordError(password)
print("密码强度符合要求。")
try:
check_password("123")
except InvalidPasswordError as e:
print(f"密码设置失败: {e.message} (你输入的密码是: {e.password})")
输出:
密码设置失败: 密码太短,至少需要8个字符 (你输入的密码是: 123)
| 概念 | 关键字/语法 | 作用 |
|---|---|---|
| 异常报告 | Traceback |
Python 解释器打印的错误信息,用于定位问题。 |
| 异常捕获 | try...except...else...finally |
防止程序因错误而崩溃,并优雅地处理错误。 |
| 主动抛出 | raise |
在代码逻辑中主动触发一个异常,强制调用者处理。 |
| 异常信息 | as e |
在 except 块中获取异常对象,以便访问其类型、消息等。 |
| 自定义异常 | class MyError(Exception): |
创建特定业务逻辑的异常,使错误处理更清晰。 |
掌握 Python 的异常处理机制,是编写健壮、可靠代码的必备技能,下次看到 Traceback 时,不要害怕,把它看作是 Python 在帮你“导航”,告诉你哪里出了问题。
