异常处理
约 986 字大约 3 分钟
2026-08-19
异常的捕获
使用 try...except 捕获可能发生的异常
可以捕获多个异常,并获取异常对象:
try:
number = int("abc")
except ValueError as e:
print(f"数值错误: {e}")
except TypeError as e:
print(f"类型错误: {e}")
else:
print("没有异常时执行")
finally:
print("始终会执行")try:
number = int("abc")
except ValueError as e:
print(e.args) # 获取异常信息
print(e.__traceback__) # 异常的堆栈跟踪对象异常类型
Python 内置异常形成层次结构,捕获父类异常可以捕获其所有子类:
BaseException
├── SystemExit # sys.exit() 引发
├── KeyboardInterrupt # Ctrl+C 引发
└── Exception # 常规异常的基类
├── ArithmeticError
│ └── ZeroDivisionError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── TypeError
├── ValueError
│ └── UnicodeError
└── ...# 捕获 Exception 可以捕获几乎所有常规异常
try:
# 可能引发各种异常的操作
pass
except Exception as e:
print(f"发生错误: {e}")
# 但不推荐捕获过于宽泛的异常,应尽量精确主动抛出异常
使用 raise 主动抛出异常:
def withdraw(balance, amount):
if amount > balance:
raise ValueError("余额不足")
if amount <= 0:
raise ValueError("取款金额必须大于零")
return balance - amount
try:
withdraw(100, 200)
except ValueError as e:
print(e) # 余额不足可以重新抛出当前异常:
try:
risky_operation()
except Exception:
# 记录日志后继续抛出
print("发生异常,准备抛出")
raise # 重新抛出自定义异常
通过继承 Exception 或其子类创建自定义异常:
class ValidationError(Exception):
"""参数验证失败"""
pass
class NotFoundError(Exception):
"""资源不存在"""
def __init__(self, resource, resource_id):
self.resource = resource
self.resource_id = resource_id
super().__init__(f"{resource} (id={resource_id}) 不存在")
# 使用
def get_user(user_id):
if user_id <= 0:
raise ValidationError("用户ID必须大于零")
if user_id not in user_database:
raise NotFoundError("User", user_id)
return user_database[user_id]异常链
def exception_chains1():
# 方式1:直接抛出(无关联)
try:
raise ValueError("错误A")
except ValueError:
raise RuntimeError("错误B") # 隐式关联,__context__ 有值
def exception_chains2():
# 方式2:from 显式关联
try:
raise ValueError("错误A")
except ValueError as e:
raise RuntimeError("错误B") from e # 显式关联,__cause__ 有值
# 查看区别
try:
exception_chains1()
except RuntimeError as e:
print("隐式关联:", e)
print(f" __cause__: {e.__cause__}") # None
print(f" __context__: {e.__context__}") # ValueError
try:
exception_chains2()
except RuntimeError as e:
print("\n显式关联:", e)
print(f" __cause__: {e.__cause__}") # ValueError
print(f" __context__: {e.__context__}") # None作业(可使用AI)
一、实现安全的除法函数
def safe_divide(a, b):
"""
安全除法,要求:
1. 捕获 ZeroDivisionError,返回 0
2. 捕获 TypeError,打印"参数类型错误"并返回 None
"""
# 你的代码
pass
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # 0
print(safe_divide("10", 2)) # 参数类型错误,None二、实现重试装饰器
import time
def retry(max_attempts, delay=1):
"""
失败重试装饰器
如果函数抛出异常,等待 delay 秒后重试,最多重试 max_attempts 次
"""
# 你的代码
pass
@retry(max_attempts=3, delay=1)
def unstable_function():
"""模拟不稳定的操作"""
import random
if random.random() < 0.7: # 70% 概率失败
raise ConnectionError("连接失败")
return "成功"
# 应该能处理失败并重试,最终返回"成功"或抛出最后一次异常三、自定义异常与验证
class InsufficientFundsError(Exception):
"""余额不足"""
pass
class AccountFrozenError(Exception):
"""账户已冻结"""
pass
class BankAccount:
def __init__(self, balance=0, frozen=False):
self.balance = balance
self.frozen = frozen
def withdraw(self, amount):
"""
取款,要求:
1. 如果 frozen=True,抛出 AccountFrozenError
2. 如果 amount > balance,抛出 InsufficientFundsError
3. 如果 amount <= 0,抛出 ValueError
"""
# 你的代码
pass
def deposit(self, amount):
"""
存款,要求:
1. 如果 frozen=True,抛出 AccountFrozenError
2. 如果 amount <= 0,抛出 ValueError
"""
# 你的代码
pass
# 测试
account = BankAccount(100)
account.deposit(50) # balance = 150
account.withdraw(30) # balance = 120
# account.withdraw(200) # InsufficientFundsError
# account.deposit(-10) # ValueError
frozen_account = BankAccount(100, frozen=True)
# frozen_account.withdraw(10) # AccountFrozenError四、异常转换
实现一个函数,将各种异常转换为统一的 APIException:
class APIException(Exception):
def __init__(self, code, message):
self.code = code
self.message = message
super().__init__(message)
def call_api():
"""模拟API调用,可能抛出各种异常"""
import random
errors = [
ValueError("参数错误"),
ConnectionError("连接超时"),
TimeoutError("请求超时"),
RuntimeError("服务器内部错误")
]
raise random.choice(errors)
def robust_api_call():
"""
调用 call_api(),将各种异常转换为 APIException:
- ValueError → APIException(400, "参数错误")
- ConnectionError/TimeoutError → APIException(503, "服务不可用")
- 其他异常 → APIException(500, "服务器内部错误")
"""
# 你的代码
pass