Python3 写异步 IO 方便吗?跟 NodeJS 比,有哪些不足之处。

2021-08-23 17:36:34 +08:00
 balabalaguguji

最近了解了下 Python3 的 async/await 用起来跟 NodeJS 的差不多,找到的异步 Redis 和 Mongodb 库都还不错。

发现 requests 没有异步,想找个替代的,aiohttp 的语法太奇怪了,如下,得先创建一个 session,然后 xxx,写起来很是麻烦,特别是要把以前的同步代码改为异步的,突然想要放弃。

import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession() as session:
        pokemon_url = 'https://pokeapi.co/api/v2/pokemon/151'
        async with session.get(pokemon_url) as resp:
            pokemon = await resp.json()
            print(pokemon['name'])

asyncio.run(main())

NodeJS 的 Promise 就非常爽,没有异步的自己包装下就好了,像 sleep 。

3735 次点击
所在节点    程序员
34 条回复
Vegetable
2021-08-23 17:40:57 +08:00
异步网络请求库都是这模样。
https://github.com/encode/httpx

> HTTPX builds on the well-established usability of requests
HongTang
2021-08-23 17:43:16 +08:00
python 不是假并发吗
kasheemlew
2021-08-23 17:49:52 +08:00
python 也可以这样:

```python
import asyncio
import requests

async def main():
loop = asyncio.get_event_loop()
futures = [
loop.run_in_executor(None, requests.get, 'http://www.baidu.com')
for _ in range(100)
]
await asyncio.gather(*futures)

asyncio.run(main())
```
libook
2021-08-23 18:13:24 +08:00
只要从同步代码重构为 async/await,基本都是要一层一层都改成 async/await 写法,包括 JS 在内的各个语言都是这样的。

aiohttp 的这个流程跟 Node.js 的 http module 基本是一致的,都是:
1. 创建 HTTP 客户端;
2. 创建请求;
3. 向请求流中写入数据,然后发送流结束;
4. 从返回流接收数据,直到流结束。
balabalaguguji
2021-08-23 18:14:29 +08:00
@libook #4 axios 好用,python 没找到那么好用的。
LeeReamond
2021-08-23 18:15:51 +08:00
1 、requests 的正常用法也包括创建 session,不用只是因为写的 demo 需求场景太简单了而已。

2 、因为 Python 存在同步宇宙与异步宇宙两种东西,你要在同步宇宙里创造异步宇宙 j 就需要手搓一个事件循环,所以采用了这种写法。node 的事件循环是与生俱来送给你的,所以你在 node 中不需要额外写法,但同样地这令进入同步宇宙变得困难。

3 、Python 也可以非常简单地将同步逻辑封装为协程。
aladdinding
2021-08-23 18:16:02 +08:00
使用了 session 应该是会和浏览器一样进行 tcp 的连接复用 跟浏览器一样
aladdinding
2021-08-23 18:16:29 +08:00
requests 也有 session
janxin
2021-08-23 18:17:24 +08:00
最大问题是侵入性的,至少要做额外适配

客户端推荐 httpx
keepeye
2021-08-23 18:36:51 +08:00
自己可以封装一下,例如

async def post(url, data: bytes, proxy=None, **kwargs):
async with aiohttp.ClientSession().post(url, data=data, proxy=proxy, **kwargs) as response:
return response
keepeye
2021-08-23 18:37:26 +08:00
缩进怎么没了 ,谁知道评论怎么发代码?
```
async def post(url, data: bytes, proxy=None, **kwargs):
async with aiohttp.ClientSession().post(url, data=data, proxy=proxy, **kwargs) as response:
return response
```
balabalaguguji
2021-08-23 18:46:23 +08:00
@keepeye #10 好嘞,感谢
ClericPy
2021-08-23 20:39:44 +08:00
我是自己缓存一个 Session 对象然后到处引用...

比如这句 async with aiohttp.ClientSession() as session 拆成 self.session = aiohttp.ClientSession() 然后 await self.session.__aenter__() 到 结束时候 await self.session.__aexit__(None, None None) 就行了

用起来还凑合吧, 至于 httpx, 性能比 aiohttp 差一倍多, 不得不选了后者, 然后自己加了 wrapper 把 aiohttp 打包成 requests 那个用法...
ysc3839
2021-08-23 20:42:53 +08:00
@LeeReamond “需要手搓一个事件循环”这是 Python 加的限制。理论上无栈协程是可以直接替代回调函数使用的,不需要什么事件循环。比如 C++的协程在 await 一个对象的时候,被 await 的对象能拿到这个协程的回调函数,执行这个回调函数就能恢复协程执行。
js 也与此类似,它的异步跟事件循环关系并不大。
但是 Python 的不一样,它一定要一个事件循环或者说调度器才能跑起来,不能像 C++那样让被等待方控制恢复执行。
ericls
2021-08-23 21:06:07 +08:00
测过 uvloop 性能和 nodejs 一模一样. 但是 CPU heavy 的东西还是慢一些
crclz
2021-08-23 21:43:39 +08:00
一点也不奇怪,每一个 async,await 都有其存在的价值。只要理解了就好了。

https://gist.github.com/crclz/2308cc72cc3f37836a6cab22c1981849
iyaozhen
2021-08-23 23:31:16 +08:00
个人感觉新业务可以用
但前提你要保证你自己确实对这个深入理解了,不然出问题没人帮你 java 那帮人不了解这个

因为 Python 天生是同步的,一旦出了问题你就得背锅了,之前有个对口的同事就这样走人了
chenqh
2021-08-23 23:40:44 +08:00
@iyaozhen 这么惨?
kuangwinnie
2021-08-24 07:00:10 +08:00
@keepeye 评论不能发代码
abersheeran
2021-08-24 09:09:20 +08:00
跟 Nodejs 比的不足之处大抵在于 Nodejs 天然自带一个 loop,Python 需要你显式创建 loop 。而且 Nodejs 里原生都是异步的,不需要自己注意。Python 里原生都是同步的,需要自己时刻注意。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/797526

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX