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