三行代码,让Python程序学会“死缠烂打”:tenacity模块深度实战
写爬虫时最怕什么?不是反爬,而是网络波动导致的随机失败。每次手动重试?写while循环?太丑了。今天聊一个专门“死缠烂打”的库——tenacity,只用几行装饰器,就能让函数自动重试,直到成功或达到条件。
🎯 基础重试:3秒后又是一条好汉
最常用场景:调用外部API时,偶发性超时或502。用@retry装饰器,不侵入业务逻辑。
from tenacity import retryimport requests@retrydeffetch_data(): resp = requests.get('https://api.example.com/data', timeout=1)return resp.json()
执行效果:
# 第一次请求超时,自动重试# 第二次成功,返回JSON数据
⏱️ 固定间隔重试:给服务器留口气
如果失败后立即重试,可能加重服务器负担。设置wait参数,每次重试前固定等待2秒。
from tenacity import retry, wait_fixed@retry(wait=wait_fixed(2))defcall_service():print("调用服务中...")raise ConnectionError("服务繁忙")
运行结果:
调用服务中... # 失败,等待2秒调用服务中... # 再次失败,再等2秒# 如此反复,直到成功或达到次数上限
🔢 限制重试次数:别一条路走到黑
无限重试可能让程序卡死。用stop=stop_after_attempt(3),最多尝试3次,失败就彻底放弃。
from tenacity import retry, stop_after_attempt@retry(stop=stop_after_attempt(3))defunstable_task():import randomif random.choice([True, False]):raise ValueError("随机失败")return"成功"
输出示例:
尝试第1次 → 失败尝试第2次 → 失败 尝试第3次 → 成功,返回"成功"
🎲 指数退避:重试界的“绅士”
高频重试可能被判定为攻击。用指数退避,等待时间逐步翻倍(1s→2s→4s),优雅且安全。
from tenacity import retry, wait_exponential@retry(wait=wait_exponential(multiplier=1, min=1, max=10))defrate_limited_api():# 模拟限流接口raise TimeoutError("限流了")
执行节奏:
第1次失败,等待1秒第2次失败,等待2秒第3次失败,等待4秒# 直到成功或达到最大等待10秒
🆚 对比其他重试库
retrying库已废弃,backoff库功能类似但API设计不如tenacity直观。tenacity的优势在于装饰器参数清晰、支持异步、可自定义重试条件(比如只对特定异常重试)。小缺点:依赖较多,但瑕不掩瑜。建议:网络请求、数据库操作、文件读写等场景,一律用tenacity替换手写重试代码。
📦 总结
tenacity把“重试”这件事抽象成了声明式配置,代码更干净、逻辑更清晰。从固定等待到指数退避,从次数限制到异常过滤,几乎覆盖所有失败恢复场景。如果你还在写try...except套while,是时候试试这个“粘人精”了。
你在项目里遇到过哪些“必须重试”的坑?评论区分享你的故事,一起避坑!🚀