异常

异常(Exception)是程序运行时出现的错误事件,它会中断正常执行流程,并转入异常处理机制

例如:

1
print(10 / 0)  # ZeroDivisionError

输出:

1
ZeroDivisionError: division by zero

程序遇到错误时,Python 会自动创建一个异常对象并抛出(raise),如果没有被捕获(handled),程序就会终止。

基本结构

Python 的异常处理使用 tryexceptelsefinally 四个关键字。

1
2
3
4
5
6
7
8
try:
# 可能出错的代码
except SomeError:
# 捕获异常
else:
# 没出错时执行
finally:
# 无论是否出错都会执行

📘 示例

1
2
3
4
5
6
7
8
9
10
11
try:
x = int(input("请输入数字:"))
result = 10 / x
except ValueError:
print("输入不是数字!")
except ZeroDivisionError:
print("不能除以零!")
else:
print("结果是:", result)
finally:
print("程序结束。")

✅ 输出举例:

1
2
3
请输入数字:0
不能除以零!
程序结束。

传播机制

当异常发生时:

  1. Python 从当前 try 块开始查找匹配的 except
  2. 如果当前函数没有处理,异常会沿调用栈向上传递
  3. 若直到顶层都未处理 → 程序终止并打印 Traceback。

📘 示例

1
2
3
4
5
6
7
8
9
10
def f1():
raise ValueError("出错啦!")

def f2():
f1()

try:
f2()
except ValueError as e:
print("捕获异常:", e)

输出:

1
捕获异常: 出错啦!

捕获异常

捕获特定异常

1
2
3
4
try:
...
except ZeroDivisionError:
...

捕获多个异常

1
2
3
4
try:
...
except (ValueError, TypeError):
...

捕获所有异常

1
2
3
4
try:
...
except Exception as e:
print("出错:", e)

注意Exception 是所有内建异常的父类(除了系统级,如 KeyboardInterrupt)。

不捕获,只查看异常信息

1
2
3
4
5
6
import traceback

try:
1 / 0
except Exception as e:
traceback.print_exc()

抛出异常

有时你需要主动触发异常,而不是等错误发生:

1
2
3
4
def divide(a, b):
if b == 0:
raise ValueError("除数不能为 0")
return a / b

raise 会立即中断函数执行,并抛出指定异常对象。

自定义异常类

所有自定义异常类都应继承自 Exception 或其子类。

1
2
3
4
5
6
7
8
9
class MyError(Exception):
"""自定义异常"""
def __init__(self, message):
super().__init__(message)

try:
raise MyError("自定义异常发生!")
except MyError as e:
print("捕获到:", e)

输出:

1
捕获到: 自定义异常发生!

异常链

异常链是指在处理一个异常时又引发另一个异常,Python 会自动或显式地保留原始异常的上下文,形成“异常链”。

隐式异常链

except 块中又抛出新的异常时,Python 自动建立链条(__context__)。

1
2
3
4
try:
1 / 0
except ZeroDivisionError:
raise ValueError("计算错误")

输出:

1
2
3
4
5
6
7
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:
...
ValueError: 计算错误

这就是隐式异常链:第二个异常的 __context__ 指向第一个异常。

显式异常链

可以用 raise ... from ... 显式指定异常链:

1
2
3
4
try:
1 / 0
except ZeroDivisionError as e:
raise ValueError("新异常") from e

输出:

1
2
3
4
ValueError: 新异常

The above exception was the direct cause of the following exception:
ZeroDivisionError: division by zero

from e 明确告诉解释器:ValueError 是由 ZeroDivisionError 直接导致的。

断开异常链

有时不想显示前一个异常,可以用 from None

1
2
3
4
try:
1 / 0
except ZeroDivisionError:
raise ValueError("忽略原异常") from None

输出:

1
ValueError: 忽略原异常

这在封装库时非常常见,用于隐藏底层细节。

异常对象

每个异常对象都有一些有用的属性:

属性 含义
args 异常的参数元组
__cause__ 显式异常链的原因
__context__ 隐式异常链的原始异常
__traceback__ traceback 对象(堆栈信息)

📘 示例:

1
2
3
4
5
6
7
8
try:
try:
1 / 0
except ZeroDivisionError as e1:
raise ValueError("新异常") from e1
except Exception as e2:
print("cause:", e2.__cause__)
print("context:", e2.__context__)

finally 与 else

finally

finally 子句无论是否出错都会执行,常用于清理资源:

1
2
3
4
5
6
7
8
try:
f = open("data.txt")
data = f.read()
except FileNotFoundError:
print("文件不存在")
finally:
f.close()
print("文件已关闭")

else

else 仅在 try 块未发生异常 时执行:

1
2
3
4
5
6
7
8
9
try:
print("计算中...")
x = 1 / 1
except ZeroDivisionError:
print("出错")
else:
print("一切正常")
finally:
print("结束")

输出:

1
2
3
计算中...
一切正常
结束

常见问题

陷阱 说明
捕获太宽泛 except Exception: 会吞掉不必要的错误
忘记 re-raise 处理后不重新抛出会隐藏问题
滥用 from None 会导致调试困难
不使用 finally 导致资源未释放

总结表

概念 说明 关键点
try-except 捕获异常 可有多个 except
else 无异常时执行 放在 except 后
finally 始终执行 清理资源
raise 主动抛异常 可指定异常对象
from 指定异常链 raise NewError from OldError
隐式链 except 中又抛异常 自动建立 __context__
显式链 from e 建立 __cause__
断链 from None 隐藏原异常