有没有理由在Python asyncio中使用`return await...`?

31
我知道在JS中,return语句前加上await没有任何作用(即return await ...),但在Python中是否也是如此,或者这样做会使材料化更有可能或不同?
如果两者不相等,那么最佳实践是什么?

这个回答解决了你的问题吗?何时使用和何时不使用Python 3.5的`await`? - shaik moeed
@shaikmoeed 这个问题更具体,值得有自己的答案(在js中有一个linter可以防止这种模式,这说明人们正在思考这个特定的事情)。 - Uri
2
@Uri "我知道在js中,在return语句前加上await没有任何添加效果",这并不是真的,虽然很接近。请阅读此文章: https://jakearchibald.com/2017/await-vs-return-vs-return-await/ 因此,经验法则应该是:始终使用await。我不确定Python如何处理它,但是Js只是一门糟糕的语言,在幕后执行许多隐式、反直觉和难以调试的操作。 - freakish
在Haskell中,return是用来在Monad中包装一个值的。所以如果你的值已经是异步的,你甚至不需要一个return调用。然而在JavaScript和Python中,你必须始终有一个return调用,所以如果你在调用return之前不使用"await"来"解包"异步monad,你最终会得到一个双重包装的值。 - undefined
2个回答

41

给定:

async def foo() -> str:
    return 'bar'

调用foo函数后得到的是一个Awaitable对象,显然需要使用await操作符。你需要考虑的是你的函数的返回值。例如,你可以这样做:

def bar() -> Awaitable[str]:
    return foo()  # foo as defined above

在这里,bar是一个同步函数但返回一个Awaitable,该函数将返回一个str

async def bar() -> str:
    return await foo()

上面,bar 本身是 async 的,并在调用时产生了一个 Awaitable,结果与上述相同,都是一个 str。这两种用法之间没有真正的区别。区别出现在下面:
async def bar() -> Awaitable[str]:
    return foo()

在这个例子中,调用 bar 会返回一个 Awaitable,它会再次返回一个 Awaitable,最终返回一个 str;与之前截然不同。如果你天真地使用上述代码,你将得到这种结果:

>>> asyncio.run(bar())
<coroutine object foo at 0x108706290>
RuntimeWarning: coroutine 'foo' was never awaited

通常情况下,每次调用 async 函数都必须在某个位置进行 await。如果你有两个 async 函数 (async def fooasync def bar),但是在 bar 中没有 await,那么调用 bar 的调用者必须进行两次 await,这将会很奇怪。


谢谢!asyncio合并协程,只需要一个await会更有意义吗?这似乎是一个奇怪的设计选择。 - Uri
5
Python 的格言是“明确胜于隐晦”,因此每个 async 只有一个 await 是非常合理的,这样可以让你完全掌控。 - deceze

17
@deceze的回答要点如下:是的,有这样的理由。在调用另一个协程时,总是要使用return await来返回一个协程。 Async函数即使使用普通的return也会返回一个可等待对象(Awaitable),只有通过调用await才能获得实际结果。如果没有return await,则结果将是多余的包装后的Awaitable,并且必须等待两次。参见文档。
import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

asyncio.run(main())


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接