什么机制让 Python lambdas 在没有 await 关键字的情况下工作?
·
回答问题
我刚刚注意到一些令人惊讶的事情。考虑以下示例:
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,所以这似乎是一个改善人体工程学的功能,但它给我带来了一些问题:
-
这究竟是如何工作的?这个简单的在任何协程函数之前“注入”一个
await吗? -
这是否记录在某处(PEP)?
-
这还有其他影响吗?我们可以安全地从 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)
更多推荐

所有评论(0)