I just noticed something surprising. Consider the following example:
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))
When we run this, we rightfully receive the following warning:
awaitable_lambda.py:5: RuntimeWarning: coroutine 'sleep' was never awaited
asyncio.sleep(n)
This is because in wait_n we called asyncio.sleep(n) without await.
But now consider the second example:
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)))
This time we are using a lambda and surprisingly the code works just fine even though there is no await.
I understand that we can not use await inside a Python lambda expression so this seems like a feature to improve ergonomics but it leads to me some questions:
await before any coroutine function?Any asynchronous function returns an awaitable. You don't need to "await the function call" immediately, you just need to await the returned awaitable value eventually. I.e., these two are equivalent:
await asyncio.sleep(1)
awaitable = asyncio.sleep(1)
await awaitable
As such, it should be easy to see that the call fn(1) of the lambda (implicitly) returns an awaitable, and await awaits it. The async def wait_n on the other hand never returns the sleep awaitable and never awaits it itself.
As a corollary example of this, if you have any wrapper around an async function, there's not necessarily a need for that wrapper to be async itself:
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With