If at all, there's no functional differences (besides syntax) between
Before 3.5, there was no real difference between a generator and a co-routine. A generator is a co-routine purely by using
yield as an expression and expecting data via
This sort-of changed with the new
async syntax added to Python 3.5. The
async syntax is both syntactic sugar (turning generators into async coroutines visually in your code) and an extension to the language to make it possible to use coroutines as context managers and iterables (
async with and
Quoting the PEP:
It is proposed to make coroutines a proper standalone concept in Python, and introduce new supporting syntax. The ultimate goal is to help establish a common, easily approachable, mental model of asynchronous programming in Python and make it as close to synchronous programming as possible.
Under the hood, co-routines are basically still generators and generators support most of the same functionality. Co-routines are now a distinct type however to make it possible to explicitly test for awaitable objects, see issue 24400 for the motivation (initially this was a per-instance flag, not a distinct type).
To be clear, you can still use
.send() an a generator. But a generator is not awaitable, it is not expected to cooperate.
asyncio must support both because the library aims to be compatible with Python versions < 3.5 and thus can't rely on the new language constructs to be available.