I'm trying to do something like this:
mylist.sort(key=lambda x: await somefunction(x))
But I get this error:
SyntaxError: 'await' outside async function
Which makes sense because the lambda is not async.
I tried to use async lambda x: ...
but that throws a SyntaxError: invalid syntax
.
Pep 492 states:
Syntax for asynchronous lambda functions could be provided, but this construct is outside of the scope of this PEP.
But I could not find out if that syntax was implemented in CPython.
Is there a way to declare an async lambda, or to use an async function for sorting a list?
An async function uses the await keyword to denote a coroutine. When using the await keyword, coroutines release the flow of control back to the event loop. To run a coroutine, we need to schedule it on the event loop. After scheduling, coroutines are wrapped in Tasks as a Future object.
In Python, they are defined using the async def keyword. Much like generators, they too use their own form of yield from which is await . Before async and await were introduced in Python 3.5, we created coroutines in the exact same way generators were created (with yield from instead of await ).
Lambda functions can be invoked either synchronously or asynchronously, depending upon the trigger. In synchronous invocations, the caller waits for the function to complete execution and the function can return a value.
You can't. There is no async lambda
, and even if there were, you coudln't pass it in as key function to list.sort()
, since a key function will be called as a synchronous function and not awaited. An easy work-around is to annotate your list yourself:
mylist_annotated = [(await some_function(x), x) for x in mylist] mylist_annotated.sort() mylist = [x for key, x in mylist_annotated]
Note that await
expressions in list comprehensions are only supported in Python 3.6+. If you're using 3.5, you can do the following:
mylist_annotated = [] for x in mylist: mylist_annotated.append((await some_function(x), x)) mylist_annotated.sort() mylist = [x for key, x in mylist_annotated]
An "async
lambda
" can be emulated by combining a lambda
with an async
generator:
key=lambda x: (await somefunction(x) for _ in '_').__anext__()
It is possible to move the ( ).__anext__()
to a helper, which likely makes the pattern clearer as well:
def head(async_iterator): return async_iterator.__anext__() key=lambda x: head(await somefunction(x) for _ in '_')
Note that the sort method/function in the standard library are not async. One needs an async version, such as asyncstdlib.sorted
(disclaimer: I maintain this library):
import asyncstdlib as a mylist = await a.sorted(mylist, key=lambda x: head(await somefunction(x) for _ in '_'))
lambda ...: (...).__anext__()
patternAn "async
lambda
" would be an anonymous asynchronous function, or in other words an anonymous function evaluating to an awaitable. This is in parallel to how async def
defines a named function evaluating to an awaitable.
The task can be split into two parts: An anonymous function expression and a nested awaitable expression.
An anonymous function expression is exactly what a lambda ...: ...
is.
An awaitable expression is only allowed inside a coroutine function; however:
__anext__
method.These three parts are directly used in the "async
lambda
" pattern:
# | regular lambda for the callable and scope # | | async generator expression for an async scope # v v v first item as an awaitable key=lambda x: (await somefunction(x) for _ in '_').__anext__()
The for _ in '_'
in the async generator is only to have exactly one iteration. Any variant with at least one iteration will do.
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