
本文详解如何在 python 装饰器中获取并判断被装饰函数的返回值(如 http 状态码),实现条件化重试逻辑——仅当返回值满足特定条件(如 status_code ≥ 500)时才触发重试,避免无效重试或逻辑错位。
本文详解如何在 python 装饰器中获取并判断被装饰函数的返回值(如 http 状态码),实现条件化重试逻辑——仅当返回值满足特定条件(如 status_code ≥ 500)时才触发重试,避免无效重试或逻辑错位。
装饰器的本质是包装并增强函数行为,而非预判或拦截其返回值。常见误区是试图在装饰器定义阶段(即 @decorator 语法处)访问被装饰函数的运行时返回值——这是不可能的,因为此时函数尚未执行。真正的返回值只能在装饰器内部的包装函数(wrapper)中,通过调用 func(*args, **kwargs) 后显式获取并处理。
要实现“仅对 5xx 响应重试,跳过 4xx 等客户端错误”,关键在于:在每次调用被装饰函数后,立即检查其返回值,并据此决定是否继续重试循环。以下是修正后的专业实现:
import requests
import time
from functools import wraps
def retry_on_status_code(
status_check=lambda code: code >= 500, # 默认仅重试 5xx
retries=3,
delay=3
):
def decorator(func):
@wraps(func) # 保留原函数元信息(__name__, __doc__等)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < retries:
try:
result = func(*args, **kwargs) # ✅ 关键:执行函数并捕获返回值
# 检查返回值是否满足重试条件(例如 status_code 字段)
if hasattr(result, 'status_code'): # 若返回 Response 对象
status_code = result.status_code
elif isinstance(result, int): # 若直接返回 status_code
status_code = result
else:
raise ValueError(f"Unexpected return type: {type(result)}")
if status_check(status_code):
print(f"Status {status_code} triggers retry (attempt {attempts + 1}/{retries})")
attempts += 1
if attempts < retries: # 非最后一次尝试则休眠
time.sleep(delay)
continue # 重新执行函数
else:
print(f"Status {status_code} is acceptable — no retry needed.")
return result # ✅ 成功返回结果
except requests.exceptions.RequestException as e:
print(f"Request failed with exception: {e}. Retrying...")
attempts += 1
if attempts < retries:
time.sleep(delay)
continue
raise RuntimeError(f"Failed after {retries} attempts")
return wrapper
return decorator
# 使用示例:返回完整的 Response 对象(推荐)
@retry_on_status_code(status_check=lambda sc: sc >= 500, retries=2, delay=1)
def get_request():
return requests.get("https://httpstat.us/503") # 模拟 503 服务不可用
# 或者返回状态码整数(需确保函数逻辑一致)
@retry_on_status_code(status_check=lambda sc: sc == 500, retries=1)
def get_status_code():
response = requests.get("https://httpstat.us/500")
return response.status_code
if __name__ == "__main__":
try:
resp = get_request()
print(f"Final response status: {resp.status_code}")
except Exception as e:
print(f"Execution failed: {e}")核心要点与注意事项:
- ✅ 返回值必须在
wrapper内部调用func()后获取:装饰器无法预知函数返回值,所有逻辑必须基于运行时结果。 - ✅ 灵活的状态检查机制:通过
status_check参数传入可定制的 lambda 函数,支持任意状态码逻辑(如lambda sc: sc in (500, 502, 503, 504))。 - ✅ 区分异常与业务状态:
requests.exceptions.RequestException表示网络/连接失败,需无条件重试;而status_code是业务响应,需按策略判断。 - ⚠️ 避免副作用陷阱:若被装饰函数有副作用(如发邮件、写数据库),重复调用可能引发问题,务必确保幂等性。
- ? 替代方案提示:对于 HTTP 请求,更健壮的做法是使用
requests.adapters.HTTPAdapter配合urllib3.util.Retry,它原生支持基于状态码的重试(status_forcelist=(500, 502, 503, 504)),无需手动实现。
掌握这一模式,你不仅能精准控制重试逻辑,还能将其泛化到日志注入、性能监控、权限校验等各类装饰器场景中——关键始终是:在包装函数中调用、捕获、决策、返回。

















