抛出异常 raise
raise 语句主动抛出一个异常,通知调用方"这里出错了"。它用于验证输入、报告错误状态、转换异常类型等场景。
基本用法
def divide(a, b):
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
divide(10, 0) # ZeroDivisionError: Cannot divide by zero
raise 后面跟异常类或异常实例。如果跟类,Python 会自动创建实例:
raise ValueError # 等价于 raise ValueError()
raise ValueError("Invalid") # 创建带参数的实例
异常参数
异常构造时可以传入任意参数,通常是一个描述错误的字符串:
def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative: %d" % age)
if age > 150:
raise ValueError("Age too large: %d" % age)
return age
set_age(-5) # ValueError: Age cannot be negative: -5
重新抛出异常
在 except 块中,裸的 raise 会重新抛出当前捕获的异常:
try:
1 / 0
except ZeroDivisionError:
print "Logging error..."
raise # 重新抛出 ZeroDivisionError
这在需要记录日志或执行清理后,把异常继续传播给上层处理时很有用。
转换异常类型
有时需要把底层异常转换为更高级别的异常:
def read_config(filename):
try:
with open(filename, "r") as f:
return parse_config(f.read())
except IOError as e:
raise ConfigError("Failed to load config: %s" % e)
except ValueError as e:
raise ConfigError("Invalid config format: %s" % e)
这样调用方只需要处理 ConfigError,而不需要关心底层是文件问题还是格式问题。
Python 3 支持 raise NewException from original 保留原始异常链,Python 2 不支持这个语法。
断言 assert
assert 是简化的异常抛出,用于调试时检查不可能发生的情况:
def factorial(n):
assert n >= 0, "n must be non-negative"
if n <= 1:
return 1
return n * factorial(n - 1)
factorial(-1) # AssertionError: n must be non-negative
assert condition, message 在 condition 为假时抛出 AssertionError。注意:
assert可以用python -O禁用,不要用它做数据验证- 用户输入的验证应该用
if + raise,而不是assert
异常作为控制流
虽然异常主要用于错误处理,但 Python 也允许用它作为控制流机制:
class Found(Exception):
pass
def search(items, target):
try:
for i, item in enumerate(items):
if item == target:
raise Found(i)
except Found as e:
return e.args[0]
return -1
print search([1, 2, 3, 4, 5], 3) # 2
这种用法在深层嵌套中跳出多层循环时有用,但通常被认为是"滥用异常"。
实际应用
参数验证:
def connect(host, port):
if not isinstance(host, str):
raise TypeError("host must be string")
if not isinstance(port, int) or not (0 <= port <= 65535):
raise ValueError("port must be integer 0-65535")
# ...
状态检查:
class BankAccount(object):
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Amount must be positive")
if amount > self.balance:
raise ValueError("Insufficient funds: %d < %d" % (self.balance, amount))
self.balance -= amount
快速失败:
def process_data(data):
if data is None:
raise ValueError("data cannot be None")
if not data:
raise ValueError("data cannot be empty")
# 主逻辑...
异常 vs 返回值
| 场景 | 推荐 |
|---|---|
| 调用方必须处理错误 | 异常 |
| 错误是常见情况 | 返回值(如 None) |
| 需要传播多层 | 异常 |
| 局部错误,立即处理 | 返回值或异常均可 |
# 异常风格
def find_user(user_id):
user = db.query(user_id)
if user is None:
raise UserNotFound(user_id)
return user
# 返回值风格
def find_user_optional(user_id):
return db.query(user_id) # 找不到返回 None