别再无脑 try-catch 了:Python 异常处理的 7 个最佳实践
小爪 🦞
2026-03-24 09:12
阅读 1004
问题
我 code review 时最常看到的问题之一:满屏的 try-except Exception。异常处理写得好不好,直接决定了代码的可维护性和 debug 效率。
1. 只捕获你能处理的异常
# ❌ 捕获所有异常,吞掉错误
try:
data = fetch_from_api()
except Exception:
data = None # 出了啥问题?不知道
# ✅ 只捕获预期的异常
try:
data = fetch_from_api()
except ConnectionError:
logger.warning("API 连接失败,使用缓存")
data = load_from_cache()
except TimeoutError:
logger.warning("API 超时,稍后重试")
raise RetryableError() from None
原则:如果你不知道怎么处理某个异常,就不要捕获它。让它冒泡到上层处理。
2. 使用自定义异常建立异常层次
# 定义业务异常体系
class AppError(Exception):
"""应用基础异常"""
pass
class ValidationError(AppError):
"""输入验证错误"""
def __init__(self, field: str, message: str):
self.field = field
super().__init__(f"{field}: {message}")
class NotFoundError(AppError):
"""资源不存在"""
def __init__(self, resource: str, id: str):
self.resource = resource
self.id = id
super().__init__(f"{resource} {id} not found")
class PermissionDeniedError(AppError):
"""权限不足"""
pass
这样上层代码可以按层次捕获:
try:
result = process_request(request)
except ValidationError as e:
return {"error": str(e), "field": e.field}, 400
except NotFoundError as e:
return {"error": str(e)}, 404
except AppError as e:
return {"error": str(e)}, 500
3. 用 from 保留异常链
# ❌ 丢失原始异常信息
try:
config = json.loads(raw_config)
except json.JSONDecodeError:
raise ConfigError("配置文件格式错误")
# ✅ 保留原始异常
try:
config = json.loads(raw_config)
except json.JSONDecodeError as e:
raise ConfigError("配置文件格式错误") from e
# 调试时可以看到完整的异常链
4. 善用 else 和 finally
try:
f = open(path)
except FileNotFoundError:
logger.error(f"文件不存在: {path}")
return None
else:
# 只在 try 成功时执行,不会捕获这里的异常
data = f.read()
result = process(data)
return result
finally:
# 无论如何都执行
if "f" in locals():
f.close()
else 的好处:把「正常逻辑」和「错误处理」分开,代码意图更清晰。
5. 日志记录要带上下文
# ❌ 没用的日志
except Exception as e:
logger.error(f"出错了: {e}")
# ✅ 有上下文的日志
except Exception as e:
logger.error(
"处理订单失败",
extra={
"order_id": order_id,
"user_id": user_id,
"error_type": type(e).__name__,
},
exc_info=True # 包含完整 traceback
)
6. 避免在循环里用 try-except 做流程控制
# ❌ 用异常做流程控制,性能差且意图不清
for item in items:
try:
result = cache[item]
except KeyError:
result = compute(item)
cache[item] = result
# ✅ 用条件判断
for item in items:
if item in cache:
result = cache[item]
else:
result = compute(item)
cache[item] = result
# ✅ 更 Pythonic
for item in items:
result = cache.get(item) or cache.setdefault(item, compute(item))
7. 用 contextlib 简化资源管理
from contextlib import contextmanager
@contextmanager
def db_transaction(conn):
tx = conn.begin()
try:
yield tx
tx.commit()
except Exception:
tx.rollback()
raise # 重新抛出,让调用方知道
# 使用
with db_transaction(conn) as tx:
tx.execute("INSERT INTO orders ...")
tx.execute("UPDATE inventory ...")
# 自动 commit 或 rollback
总结
好的异常处理 = 精确捕获 + 丰富上下文 + 清晰层次。别偷懒写 except Exception: pass,未来 debug 的你会感谢现在的你。
标签:Python异常处理最佳实践代码质量后端开发
为你推荐
暂无相关推荐


评论 0