V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
balabalaguguji
V2EX  ›  程序员

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

  •  1
     
  •   balabalaguguji · Aug 23, 2021 · 4569 views
    This topic created in 1709 days ago, the information mentioned may be changed or developed.

    最近了解了下 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 。

    34 replies    2021-08-25 09:58:09 +08:00
    Vegetable
        1
    Vegetable  
       Aug 23, 2021
    异步网络请求库都是这模样。
    https://github.com/encode/httpx

    > HTTPX builds on the well-established usability of requests
    HongTang
        2
    HongTang  
       Aug 23, 2021
    python 不是假并发吗
    kasheemlew
        3
    kasheemlew  
       Aug 23, 2021
    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
        4
    libook  
       Aug 23, 2021
    只要从同步代码重构为 async/await,基本都是要一层一层都改成 async/await 写法,包括 JS 在内的各个语言都是这样的。

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

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

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

    客户端推荐 httpx
    keepeye
        10
    keepeye  
       Aug 23, 2021
    自己可以封装一下,例如

    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
        11
    keepeye  
       Aug 23, 2021
    缩进怎么没了 ,谁知道评论怎么发代码?
    ```
    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
        12
    balabalaguguji  
    OP
       Aug 23, 2021
    @keepeye #10 好嘞,感谢
    ClericPy
        13
    ClericPy  
       Aug 23, 2021
    我是自己缓存一个 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
        14
    ysc3839  
       Aug 23, 2021 via Android   ❤️ 1
    @LeeReamond “需要手搓一个事件循环”这是 Python 加的限制。理论上无栈协程是可以直接替代回调函数使用的,不需要什么事件循环。比如 C++的协程在 await 一个对象的时候,被 await 的对象能拿到这个协程的回调函数,执行这个回调函数就能恢复协程执行。
    js 也与此类似,它的异步跟事件循环关系并不大。
    但是 Python 的不一样,它一定要一个事件循环或者说调度器才能跑起来,不能像 C++那样让被等待方控制恢复执行。
    ericls
        15
    ericls  
       Aug 23, 2021
    测过 uvloop 性能和 nodejs 一模一样. 但是 CPU heavy 的东西还是慢一些
    crclz
        16
    crclz  
       Aug 23, 2021
    一点也不奇怪,每一个 async,await 都有其存在的价值。只要理解了就好了。

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

    因为 Python 天生是同步的,一旦出了问题你就得背锅了,之前有个对口的同事就这样走人了
    simple2025
        18
    simple2025  
       Aug 23, 2021
    @iyaozhen 这么惨?
    kuangwinnie
        19
    kuangwinnie  
       Aug 24, 2021
    @keepeye 评论不能发代码
    abersheeran
        20
    abersheeran  
       Aug 24, 2021   ❤️ 1
    跟 Nodejs 比的不足之处大抵在于 Nodejs 天然自带一个 loop,Python 需要你显式创建 loop 。而且 Nodejs 里原生都是异步的,不需要自己注意。Python 里原生都是同步的,需要自己时刻注意。
    qW7bo2FbzbC0
        21
    qW7bo2FbzbC0  
       Aug 24, 2021
    @crclz #16 最新的 HTTPClient 不是自带 async 方法了吗
    Nich0la5
        22
    Nich0la5  
       Aug 24, 2021
    python 异步几个蛋疼的点 await 传染,具体的异步实现依赖于第三方库而且场景覆盖不全,像 aiohttp aiofile 一直有海量 bug,( httpx 相对好一些维护的人比较多),自己从头撸一个异步库又要从底开始太麻烦了,我就是嫌麻烦才用 py 的。我自己写的时候经常是线程协程混写,只有明显协程性能占优的场景才用。

    至于你说的 session 问题,request 也有,而且推荐这种写法,你可以自己测下性能,复用 session 和不带的差距还是很大的
    wangyzj
        23
    wangyzj  
       Aug 24, 2021
    天生异步和假异步
    python 不是干这个的
    能写而已
    sss007
        24
    sss007  
       Aug 24, 2021
    python 写轮询 http 都能假死。。。
    enrolls
        25
    enrolls  
       Aug 24, 2021
    使用 3.9 版本,語法會變簡單。或者試試
    enrolls
        26
    enrolls  
       Aug 24, 2021
    使用 3.9 版本,語法會變簡單。或者試試 curio
    mmdsun
        27
    mmdsun  
       Aug 24, 2021 via Android
    async/await 还是 C sharp 最舒服。

    其他语言有 async/await 但没学到 C#异步的精髓。
    meiyoumingzi6
        28
    meiyoumingzi6  
       Aug 24, 2021
    主要还是生态吧, 感觉写起来好点, 单还是不太爽
    还是 golang 写异步爽的起飞
    Trim21
        29
    Trim21  
       Aug 24, 2021
    如果你要替换 requests 的话应可以用 httpx.AsyncClient,基本上就是把 requests.Session 的 http 请求换成了异步的。

    如果是长时间运行的服务的话本来就不应该用 requests.get ,应该整个程序初始化一个或者多个 requests.Session,然后复用 session,跟 aiohttp 强制你要做的事情是差不多的,aiohttp 的文档里面也提到了不要每次请求都创建一个 session
    lewinlan
        30
    lewinlan  
       Aug 25, 2021 via Android
    都 2021 年了,还写 py,放过自己好吗?
    balabalaguguji
        31
    balabalaguguji  
    OP
       Aug 25, 2021
    @Trim21 #29 好的,多谢。我的连接都是一次一个的,似乎没必要保持一个 session
    molika
        32
    molika  
       Aug 25, 2021
    习惯就好了 ~ 可能需要注意的就是各种三方库了 好多都是同步的。要自己 hack 打补丁 很痛苦。
    gitopen
        33
    gitopen  
       Aug 25, 2021
    @lewinlan 不写 py,写啥
    balabalaguguji
        34
    balabalaguguji  
    OP
       Aug 25, 2021
    @molika #32 想想还是放弃了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3850 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 109ms · UTC 00:47 · PVG 08:47 · LAX 17:47 · JFK 20:47
    ♥ Do have faith in what you're doing.