# Python中Timeout与TimeoutError异常详解(含requests、Tushare实战)
在Python网络编程、接口调用(如Tushare、requests)及异步开发中,`Timeout`和`TimeoutError`是高频出现的超时异常。二者名称高度相似,但**本质与适用场景完全不同**,很多开发者容易混淆,进而导致异常捕获不全面、程序重试逻辑失效。本文将从所属模块、继承关系、触发场景等核心维度深度对比二者,结合requests接口测试、Tushare金融数据调用实战,给出可直接落地的代码示例与避坑指南。
一、核心区别一览表
|对比项|`Timeout`|`TimeoutError`
所属模块`requests.exceptions`(第三方库专属,仅requests可用)Python内置异常`builtins`(系统通用,无需额外导入)
继承关系`RequestException` → `OError` → `Exception``OSError` → `Exception`
触发场景HTTP请求层面,等待服务器响应超时,系统级操作层面,如socket、异步协程、锁等待等超时
典型用法`requests.get(url, timeout=5)`超时后抛出`asyncio.wait_for()`、`socket.settimeout()`超时后抛出
二、`Timeout`异常(requests库专属)
1. 异常定义
`Timeout`是requests网络请求库自定义的异常类,完整路径为`requests.exceptions.Timeout`,仅作用于requests发起的HTTP/HTTPS请求,不属于Python内置异常,使用前需手动导入。
2. 触发条件
当使用requests发送HTTP/HTTPS请求时,通过`timeout`参数指定等待服务器响应的最长时间(单位:秒),若超过该时间仍未收到服务器的响应(包括连接建立超时、数据返回超时),则直接抛出该异常,仅影响HTTP请求流程,不涉及系统级操作。
3. 代码示例(结合文档接口实战)
以下示例调用文档中延迟5秒响应的接口(`https://httpbin.org/delay/5`),设置超时时间为2秒,模拟requests请求超时场景:
"""
python
import requests
from requests.exceptions import Timeout
def test_request_timeout():
try:
# 目标接口延迟5秒响应,设置超时2秒(小于响应延迟,必然触发超时)
resp = requests.get("https://httpbin.org/delay/5", timeout=2)
resp.raise_for_status() # 若未超时,校验HTTP状态码
except Timeout:
print("requests请求超时!2秒内未获取到服务器响应")
if __name__ == "__main__":
test_request_timeout()
"""
三、`TimeoutError`异常(Python内置通用)
1. 异常定义
`TimeoutError`是Python内置的标准异常,属于系统级通用超时异常,无需额外导入即可使用,继承自`OSError`,适用于所有系统级阻塞操作的超时场景。
2. 触发场景
该异常不局限于网络请求,主要作用于系统级阻塞操作,常见场景包括:
- 套接字(socket)操作:设置超时后,连接、读取、写入等操作超时;
- 异步编程:`asyncio.wait_for()`等待协程执行超时;
- 线程/进程同步:`threading.Lock.acquire(timeout=)`等待锁超时;
- 其他系统I/O阻塞操作:如文件读写、管道通信等超时。
3. 代码示例(异步任务超时)
以下示例模拟耗时10秒的异步任务,设置等待超时时间为3秒,触发`TimeoutError`异常:
"""
python
import asyncio
async def slow_task():
# 模拟耗时10秒的异步任务(如复杂计算、系统调用)
await asyncio.sleep(10)
async def test_async_timeout():
try:
# 等待协程执行,超时时间设为3秒(小于任务耗时,触发超时)
await asyncio.wait_for(slow_task(), timeout=3)
except TimeoutError:
print("异步任务超时!3秒内未执行完成")
if __name__ == "__main__":
asyncio.run(test_async_timeout())
"""
四、Tushare接口调用中的超时异常实战
Tushare是量化交易中常用的金融数据接口,其底层基于requests库发起HTTP请求,因此超时异常逻辑与requests高度相关,但需兼顾系统级超时场景,避免重试逻辑遗漏。
核心特点:
1. Tushare内部会封装底层socket异常,对外直接抛出的超时异常,大多为`requests.exceptions.Timeout`;
2. 若要保证重试逻辑覆盖所有超时场景,需同时捕获requests相关异常(含`Timeout`)和Python内置`TimeoutError`。
重试装饰器实战代码(可直接落地)
结合`tenacity`重试库,编写带重试机制的Tushare日线数据获取函数,兼容两种超时异常,提升接口调用稳定性:
"""
python
import tushare as ts
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from requests.exceptions import Timeout, RequestException # 导入requests相关异常
# 初始化Tushare(请替换为自己的个人token,从Tushare官网获取)
ts.set_token("your_tushare_token")
pro = ts.pro_api()
@retry(
stop=stop_after_attempt(3), # 最大重试3次,避免无限重试
wait=wait_exponential(multiplier=1, min=2, max=10), # 指数退避:初始2秒,最大10秒,减轻接口压力
retry=retry_if_exception_type((RequestException, TimeoutError)) # 捕获请求异常+两种超时异常
)
def get_daily_with_retry(ts_code, trade_date):
"""带重试机制的Tushare日线数据获取函数,适配超时异常"""
# 调用Tushare日线接口,底层基于requests发起请求
return pro.daily(ts_code=ts_code, trade_date=trade_date)
# 调用示例(获取平安银行2024年1月1日日线数据)
if __name__ == "__main__":
try:
data = get_daily_with_retry(ts_code="000001.SZ", trade_date="20240101")
print("获取日线数据成功:")
print(data)
except (RequestException, TimeoutError):
print("多次重试后仍超时,获取数据失败,请检查网络或接口状态")
"""
五、总结(核心避坑要点)
1. **`Timeout`** **:HTTP请求层专属** - 仅requests等网络库使用,对应HTTP请求等待服务器响应超时,需导入`requests.exceptions.Timeout`才能捕获;
2. **`TimeoutError`** **:系统层通用内置** - 不局限于网络请求,对应socket、异步、锁等待等系统级操作超时,无需导入即可直接捕获;
3. **Tushare开发避坑** - 优先捕获`requests.exceptions.RequestException`(该异常已包含`Timeout`),同时补充捕获`TimeoutError`,确保所有超时场景都能被覆盖,避免程序因未捕获异常而崩溃。
明确二者的区别后,可精准设计异常捕获逻辑,结合重试机制,大幅提升Python接口调用(尤其是网络请求、金融数据接口)的稳定性和健壮性。

358

被折叠的 条评论
为什么被折叠?



