- Define the function:
def cor():
hi = yield "Hello"
yield hi
- Create an instance:
cor = cor()
- Use next() to process the function:
print(next(cor))
- Use send() to provide an input value to the function:
print(cor.send("World"))
- This is what it looks like put together:
- To make life easier and avoid having to manually call next() every time, coroutine_decorator.py shows how a decorator can be made to handle the iteration for us:
def coroutine(funct):
def wrapper(*args, **kwargs):
cor = funct(*args, **kwargs)
next(cor)
return cor
return wrapper
According to the official documentation (https://docs.python.org/3/library/asyncio-task.html#coroutines), it is preferable to use @asyncio.coroutine to decorate generator-based coroutines. It isn't strictly enforced, but it enables compatibility with async def coroutines and also serves as documentation.
- asyncio_concurrent.py, from https://docs.python.org/3/library/asyncio-task.html#example-chain-coroutines, shows how to use asyncio to perform concurrent processing:
import asyncio
async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
-
- The event loop is started (get_event_loop()) and calls print_sum()
- The print_sum() coroutine is suspended while it calls compute()
- The compute() coroutine starts but immediately goes to sleep for 1 second
- When compute() restarts, it finishes its computation and returns the result
- The print_sum() coroutine receives the result and prints it
- There are no more computations to perform so the print_sum() coroutine raises the StopIteration exception
- The exception causes the event loop to terminate and the loop is closed
- Here is asyncio_multi_jobs.py (https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks) that shows a better illustration of the concurrent execution of multiple jobs:
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number+1):
print("Task %s: Compute factorial(%s)..." % (name, i))
await asyncio.sleep(1)
f *= i
print("Task %s: factorial(%s) = %s" % (name, number, f))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
))
loop.close()
In this example, three factorial coroutines are created. Because of the asynchronous nature of the code, they aren't necessarily started in order, nor are they processed and completed in order.
- Your results may vary, but here is an example of the output of this code:
As can be seen, the jobs were started in reverse order; if you look at the official documentation, they were started in order. Each task was completed at a different time so, while the results are in order, each individual task took a varying amount of time. This can also be seen when compared to the official documentation.