Python 3.5 greatly expanded support for asynchronous programming with a new function definition syntax. Whereas async functions were previously just "generators with benefits":
def generate_numbers():
"""
Generator function that lazily returns 1 - 100
"""
for i in range 100:
yield i
generate_async = asyncio.coroutine(generate_numbers)
generate_async.__doc__ = """
Coroutine that lazily returns 1 - 100
This can be used interchangeably as a generator or a coroutine
"""
they now have their own special declaration syntax and special behavior by which they are no longer usable as usual generator functions:
aysnc def generate_async_native():
"""
A coroutine that returns 1 - 100
This CANNOT be used as a generator, and can ONLY be executed by running it from an event loop
"""
for i in range(100):
await i
This is not a question about the functional or practical differences between these types -- that is discussed in this StackOverflow answer.
My question is: why would I ever want to use async def
? It seems like it provides no additional benefit over @asyncio.coroutine
, but imposes an additional cost in that it
async def
won't even parse in older versions, although this is arguably a feature and not a bug) andOne possible answer is given by Martijn Pieters:
The advantages are that with native support, you can also introduce additional syntax to support asynchronous context managers and iterators. Entering and exiting a context manager, or looping over an iterator then can become more points in your co-routine that signal that other code can run instead because something is waiting again
This has in fact come to fruition with new async with
and async for
syntax, which cannot be as easily implemented with a "tack-on" solution like a decorated generator.