回答问题

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

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))

当我们运行它时,我们理所当然地收到以下警告:

awaitable_lambda.py:5: RuntimeWarning: coroutine 'sleep' was never awaited

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)))

这次我们使用的是lambda,令人惊讶的是,即使没有await,代码也能正常工作。

我知道我们不能在 Pythonlambda表达式中使用await,所以这似乎是一个改善人体工程学的功能,但它给我带来了一些问题:

  1. 这究竟是如何工作的?这个简单的在任何协程函数之前“注入”一个await吗?

  2. 这是否记录在某处(PEP)?

  3. 这还有其他影响吗?我们可以安全地从 lambda 表达式调用协程函数并依靠 Python 为我们等待事情吗?

Answers

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

await asyncio.sleep(1)
awaitable = asyncio.sleep(1)
await awaitable

因此,应该很容易看出 lambda 的调用fn(1)(隐式)返回一个可等待对象,并且await等待它。另一方面,async def wait_n永远不会返回可等待的sleep,也永远不会等待它本身。

作为这方面的一个必然示例,如果您有任何围绕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)
Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐