装饰器中异常处理必须在返回的wrapper函数内用try/except捕获,而非装饰器定义时;需透传args/*kwargs、return原函数返回值、区分异常类型、避免吞掉BaseException、支持参数校验与回调扩展,并为异步函数单独实现。

装饰器里用 try/except 包住函数调用就行
核心就一句话:try 执行原函数,except 捕获异常并做统一处理。别写成在装饰器定义时就 try,那会直接报错——得在返回的闭包函数里捕获。
常见错误是把异常处理逻辑写在装饰器工厂函数内部,而不是内层的 wrapper 里。结果函数还没调用,异常就抛出来了。
- 装饰器必须返回一个新函数(通常是
wrapper),异常捕获逻辑放在这里面 - 记得用
*args, **kwargs透传参数,否则被装饰函数一有参数就报TypeError - 别忘了
return原函数的返回值,否则所有被装饰函数都变成None
需要区分异常类型?在 except 后加具体异常类
统一处理不等于“全吃掉”。比如 ValueError 可能要记录日志后继续抛出,而 ConnectionError 可以重试三次再放弃。
硬写死 except Exception: 很危险——会吞掉 KeyboardInterrupt、SystemExit,导致 Ctrl+C 失效。
- 优先捕获具体异常,如
except requests.RequestException: - 真要兜底,用
except BaseException:而不是Exception,但之后得手动判断是否该重新抛出 - 重试逻辑建议用
for _ in range(3):+break,别用递归,容易栈溢出
想让装饰器支持传参(比如指定重试次数)?加一层闭包
带参装饰器本质是“装饰器工厂”,返回真正的装饰器。用户写 @handle_exception(retries=2),实际调用的是工厂函数。
容易踩的坑是参数校验没做:比如传了负数重试次数,或者日志级别字符串拼错了,运行时才暴露。
- 工厂函数(最外层)接收装饰器参数,做类型和范围校验
- 中间层返回真正的装饰器函数(接收被装饰函数)
- 最内层
wrapper才执行逻辑,此时可安全使用外层传入的参数 - 别漏掉
@functools.wraps(func),否则被装饰函数的__name__、__doc__全变成wrapper
日志、告警、降级这些业务逻辑怎么塞进去?用回调函数传进来
把处理动作抽成函数参数,比在装饰器里硬编码更灵活。比如测试时传个空函数,线上才连 Sentry。
很多人直接在装饰器里写 logging.error(),结果单元测试没法 mock,一跑就打屏。
- 定义默认回调,如
on_error=lambda e: None - 在
except块里调用on_error(exc),把异常对象传过去 - 降级逻辑(如返回缓存值)放在
except里,但注意别掩盖真正错误——先记录,再返回兜底值 - 如果回调里可能抛异常,建议再套一层
try/except,避免装饰器本身崩掉
最常被忽略的一点:装饰器捕获的是函数体内的异常,对异步函数(async def)完全无效。要用 async def wrapper + await func(...) 重写,不能混用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











