什么机制使Python lambdas无需await关键字即可工作?

Chr*_*oph 9 python lambda async-await python-asyncio

我只是注意到一些令人惊讶的事情。考虑以下示例:

import asyncio

async def wait_n(n):
    asyncio.sleep(n)

async def main(fn):
    print("meh")
    await fn(1)
    print("foo")

loop = asyncio.get_event_loop()
loop.run_until_complete(main(wait_n))
Run Code Online (Sandbox Code Playgroud)

运行此命令时,我们正确地收到以下警告:

awaitable_lambda.py:5: RuntimeWarning: coroutine 'sleep' was never awaited
Run Code Online (Sandbox Code Playgroud)

asyncio.sleep(n)

这是因为在wait_n我们中,我们asyncio.sleep(n)没有调用await

但现在考虑第二个示例:

import asyncio

async def main(fn):
    print("meh")
    await fn(1)
    print("foo")

loop = asyncio.get_event_loop()
loop.run_until_complete(main(lambda n: asyncio.sleep(n)))
Run Code Online (Sandbox Code Playgroud)

这次我们使用的是lambda,即使没有,代码也可以正常工作await

我明白,我们不能await一个Python里面lambda表情让这似乎是一个功能,可以提高工效,但它导致了我一些问题:

  1. 这是如何工作的?这个简单的“注入”是否await在任何协程功能之前?
  2. 这在某处有记录吗(PEP)?
  3. 这还有其他含义吗?我们可以安全地从lambda表达式中调用协程函数,并依靠Python来为我们等待吗?

dec*_*eze 10

任何异步函数都返回awaitable。您不需要立即“ await调用函数”,await最终只需要返回返回的等待值。即,这两个是等效的:

await asyncio.sleep(1)
Run Code Online (Sandbox Code Playgroud)
awaitable = asyncio.sleep(1)
await awaitable
Run Code Online (Sandbox Code Playgroud)

这样,应该很容易看到fn(1)lambda 的调用(隐式地)返回了一个awaitable并await等待它。在async def wait_n另一方面,从未返回sleepawaitable从不等待它本身。

作为一个必然的例子,如果您在async函数周围有任何包装,则不一定需要该包装async本身:

def add_1(func):
    def wrapper(a):
        return func(a + 1)  # passes the awaitable return value through

    return wrapper

@add_1
async def foo(a):
    await asyncio.sleep(a)

async def main():
    await foo(1)
Run Code Online (Sandbox Code Playgroud)