Python中的协程使用举例

新冠肺炎放假快一个月,新年一开盘暴跌,就又想写跟踪大盘指数的代码了……

由于GIL名声在外,所以Python的线程编程我是从来没实际操作过……不过随着Python3中各大版本对协程支持的力度加大,在同一个进程内,可以考虑通过协程增加代码的执行效率了,尤其是在I/O部分。

在Python3.6/7/8中(尤其是7/8),对协程的支持使得编写一个协程变得异常简单,这里就介绍两种最常见的情况。(本文使用Python3.8版本)

1 异步执行传统函数

举个例子,比如我希望requests.get多个页面http://www.baidu.com,可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async def make_requests():
loop = asyncio.get_event_loop()
future_list = []
url = 'http://www.baidu.com'
for i in range(5):

future = loop.run_in_executor(None, requests.get, url)
future_list.append(future)

response_list = await asyncio.gather(*future_list)
return response_list

rps_list = asyncio.run(make_requests())
print(rps_list)

主要就是将普通函数放入loop.run_in_executor中生成一个future对象,然后把这些future交给asyncio.gather去统一执行,并返回结果。

  • 注意,requests的复杂请求,如post请求或通过requests.Session设置header后再通过session.send发送的请求,使用asyncio.gather执行会报requests.exceptions.InvalidSchema异常,我猜测可能因为复杂请求调用了线程相关的适配器。

代码异步请求了5次百度页面,结果如下,用timeit测试比同步请求快了4倍左右,还是符合期望的:

1
[<Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>]

2 异步执行协程

这个就更简单了,依然用上面的例子,requests这个库通常是用在同步场景中的。如果希望既要使用异步还要享受requests的友好API,可以试试httpx,这个库包含了异步调用的请求支持,API与同步版的几乎没有差别,十分方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async def make_requests():
async with httpx.AsyncClient() as client:
url = 'http://www.baidu.com'
req_task_list, rsp_list = [], []
for i in range(5):
method, data, headers = 'GET', {}, {}
req_task_list.append(asyncio.create_task(client.request(method, url, data=data, headers=headers)))

for task in req_task_list:
rsp = await task
rsp_list.append(rsp)
return rsp_list

rps_list = asyncio.run(make_requests())
print(rps_list)

可以看到httpx直接提供了用于异步请求的AsyncCLient,即使是复杂请求也可以进行异步请求了。我们的主要工作就是通过asyncio.create_task批量创建Task,再统一await task即可。

  • 注意,始终要记得await其实就是yield的语法糖,如果创建一个task直接await,然后再创建下一个,就变成同步请求了。

结果以及获得结果的速度与第一节的例子相差无几:

1
[<Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>, <Response [200 OK]>]

3 总结

通过协程在同一个进程内进行诸如I/O请求的耗时操作,可以极大的提升代码执行效率,而且随着Python大版本的更新,将引入越来越多的异步支持。

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×