How to use await in a python lambda How to use await in a python lambda python-3.x python-3.x

How to use await in a python lambda


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 amylist = await a.sorted(mylist, key=lambda x: head(await somefunction(x) for _ in '_'))

Understanding the lambda ...: (...).__anext__() pattern

An "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:

    • An (asynchronous) generator expression implicitly creates a (coroutine) function. As an async generator only needs async to run, it can be defined in a sync function (since Python 3.7).
    • An asynchronous iterable can be used as an awaitable via its __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 awaitablekey=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 already defined a separate async function, you can simplify MisterMiyagi's answer even a bit more:

mylist = await a.sorted(    mylist,     key=somefunction)

If you want to change the key after awaiting it, you can use asyncstdlib.apply:

mylist = await a.sorted(    mylist,     key=lambda x: a.apply(lambda after: 1 / after, some_function(x)))

Here is a complete example program:

import asyncioimport asyncstdlib as aasync def some_function(x):    return xasync def testme():    mylist=[2, 1, 3]    mylist = await a.sorted(        mylist,         key=lambda x: a.apply(lambda after: 1 / after, some_function(x)))            print(f'mylist is: {mylist}')    if __name__ == "__main__":    asyncio.run(testme())