Python 异常处理
异常是运行时的错误信号。用 try / except 可以把正常流程和出错时的处理分开,避免程序直接崩溃;else、finally、raise 则用于更精细的控制。与 return 和 finally 的先后关系见 return 语句。
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零")try / except 如何工作
- 先执行
try里的语句。 - 若未发生异常,跳过所有
except。 - 若发生异常,停止
try中剩余语句,按顺序匹配except的类型;第一个匹配的子句执行后,其余except忽略。
try:
user_input = input("请输入一个整数:")
number = int(user_input)
print(f"你输入了:{number}")
except ValueError:
print("这不是合法整数")用户输入非数字时,int() 抛出 ValueError,由对应 except 处理。
多种异常
多个 except 分别处理不同类型;顺序很重要:应把更具体的异常放在更通用的之前(例如 FileNotFoundError 在 OSError 之前)。
try:
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
value = int(content)
except FileNotFoundError:
print("文件不存在")
except ValueError:
print("文件内容不是合法整数")
except PermissionError:
print("没有读权限")同一分支捕获多种异常:
def fetch_api_data():
raise ConnectionError("断网")
def process_data(data):
pass
try:
data = fetch_api_data()
process_data(data)
except (ConnectionError, TimeoutError):
print("网络请求失败")绑定异常对象:as
try:
result = 1 + 2 + "three"
except TypeError as error:
print(type(error).__name__)
print(error)
print(error.args)error 即异常实例,便于日志与调试。
else 子句
else 在 try 未抛出任何异常时执行,适合把「成功后的逻辑」与 except 分开,避免把可能引发其它类型异常的代码全塞进 try,误被外层 except 接住。
import json
def default_config():
return {}
def validate_config(data):
pass # 按项目规则校验
def load_config(path):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
print("配置文件缺失,使用默认配置")
return default_config()
else:
validate_config(data)
return data
finally:
print("配置加载流程结束")子句顺序必须是:try → except → else → finally(else / finally 可选)。
finally:清理
finally 是否发生异常都会执行,常用来关闭连接、文件等。若在 try 里 return,仍会先执行 finally 再返回;finally 里若再 return 会覆盖前面的返回值(尽量少用)。
import sqlite3
conn = None
try:
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE t (x INTEGER)")
except sqlite3.Error as e:
print("数据库错误:", e)
raise
finally:
if conn is not None:
conn.close()raise:主动抛出
def withdraw(account, amount):
if amount <= 0:
raise ValueError("取款金额必须为正")
if amount > account.balance:
raise ValueError("余额不足")
account.balance -= amount
return account.balance在 except 里写 raise(不带表达式)表示原样继续抛出当前异常,便于先打日志再交给上层:
import logging
logger = logging.getLogger(__name__)
def risky():
raise OSError("磁盘只读")
try:
risky()
except OSError as e:
logger.error("IO 失败: %s", e)
raise异常链:from / from None
class CacheError(Exception):
pass
class DataLoadError(Exception):
pass
def load_from_cache():
raise CacheError("缓存未命中")
try:
load_from_cache()
except CacheError as e:
raise DataLoadError("加载失败") from efrom e 保留原因链,回溯更清晰。
import json
try:
json.loads("{") # 非法 JSON
except json.JSONDecodeError:
raise ValueError("数据格式无效") from Nonefrom None 会隐藏原始异常,只显示新异常;会丢掉部分调试信息,谨慎使用。
以下
requests示例需pip install requests;若不想依赖第三方库,可改用urllib等标准库并捕获其异常。
import requests
from requests.exceptions import RequestException, Timeout, HTTPError
def fetch_user(user_id, max_retries=3):
url = f"https://api.example.com/users/{user_id}"
for attempt in range(max_retries):
try:
r = requests.get(url, timeout=10)
r.raise_for_status()
return r.json()
except Timeout:
print(f"超时(第 {attempt + 1}/{max_retries} 次)")
if attempt == max_retries - 1:
raise
except HTTPError as e:
if e.response.status_code == 404:
return None
raise
except RequestException as e:
print(f"请求失败: {e}")
raise自定义异常
class InsufficientFundsError(Exception):
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"无法取出 {amount},当前余额 {balance}")
class AccountLockedError(Exception):
pass继承 Exception(一般不要直接继承 BaseException,以免干扰 KeyboardInterrupt、SystemExit 等)。
实践建议
try里尽量只包「预期可能失败」的几行,不要把整个函数塞进去。- 避免裸
except::会连KeyboardInterrupt、SystemExit也吞掉。应写except Exception并记录后raise,或捕获具体类型。
import logging
logger = logging.getLogger(__name__)
def risky_operation():
raise RuntimeError("演示")
# 不推荐
try:
risky_operation()
except:
print("出错了")
# 更好
try:
risky_operation()
except Exception:
logger.exception("未预期错误")
raiseBaseException与Exception:用户自定义与日常捕获的多数是Exception子类;KeyboardInterrupt继承BaseException,不会被except Exception捕获(通常也不应去捕获它,除非有特殊需求)。
调试与回溯
回溯(traceback)从最后一行看「直接原因」,向上看调用链。在返回给前端的错误信息里是否附带内部细节,要兼顾安全与可排查性。
def calculate_total(items):
return sum(item["price"] * item["quantity"] for item in items)
def process_cart(cart_data):
try:
total = calculate_total(cart_data["items"])
return {"total": total, "status": "success"}
except KeyError as e:
return {
"total": 0,
"status": "error",
"message": f"缺少字段: {e}",
}
except TypeError as e:
return {
"total": 0,
"status": "error",
"message": f"类型无效: {e}",
}小结
| 语法 | 作用 |
|---|---|
try / except | 捕获并处理异常 |
as | 绑定异常实例 |
else | **try 无异常时执行 |
finally | 无论是否异常都执行(清理) |
raise | 抛出异常;裸 raise 继续传播 |
from / from None | 异常链或抑制链 |