Alright, coroutines those fancy functions that allow you to pause execution mid-way through and pick up where you left off later on. But before we dive in, let me first explain why you might want to use them instead of just using regular old functions.
First, they can help with concurrency by allowing multiple tasks to run simultaneously without blocking the main thread or consuming too many resources. This is especially useful for I/O-bound operations like reading from a file or making network requests. Instead of having one function block while waiting for input, you can use coroutines to handle these tasks asynchronously and switch between them seamlessly.
Secondly, they’re just plain fun! Who doesn’t love being able to pause execution mid-way through and pick up where you left off later on?
So how do coroutines work in Python specifically? Let me break it down for you.
In Python 3.5+, we have the async and await keywords which allow us to create what are called “async functions” or “coroutine functions”. These functions can be marked with the @asyncio.coroutine decorator (or simply @async def) to indicate that they’re meant to run asynchronously.
Here’s an example:
# Import the asyncio library
import asyncio
# Define an async function using the @asyncio decorator
@asyncio.coroutine
def my_coro():
# Print a message to indicate the start of the coroutine
print("Starting coroutine...")
# Use the yield from keyword to wait for 2 seconds
yield from asyncio.sleep(2)
# Print a message to indicate the end of the coroutine
print("Coroutine finished!")
# Get the event loop
loop = asyncio.get_event_loop()
# Run the async function until it is complete
loop.run_until_complete(my_coro())
# Output:
# Starting coroutine...
# Coroutine finished!
# Explanation:
# The first line imports the asyncio library, which allows us to create asynchronous functions.
# The @asyncio decorator marks the function as an async function.
# The function is defined as "my_coro" and it prints a message to indicate the start of the coroutine.
# The yield from keyword is used to wait for 2 seconds before continuing.
# After the wait, another message is printed to indicate the end of the coroutine.
# The event loop is retrieved using the get_event_loop() function.
# The loop runs until the async function is complete, which is indicated by the yield from keyword.
# The output shows that the coroutine function ran asynchronously, with the second message being printed after a 2 second delay.
In this example, we’re creating a coroutine function called `my_coro`. When it runs, it prints “Starting coroutine…” and then waits for 2 seconds using the asyncio.sleep() method (which is an awaitable object). After that, it prints “Coroutine finished!”
But what exactly does yield from do? Well, when a function inside of another asynchronous function reaches a yield statement, control is passed back to the event loop which can then switch to other tasks in the meantime. When the task resumes execution (in this case after 2 seconds), it picks up where it left off and continues running until it hits another yield statement or returns normally.
This allows us to create a sort of “cooperative multitasking” which is much more efficient than traditional preemptive multitasking because the tasks are voluntarily giving up control instead of being forcibly interrupted by an operating system.
They’re not as scary as they might seem at first, and can be a powerful tool when used correctly. Just remember to use them sparingly (too many tasks running simultaneously can lead to performance issues) and always test your code thoroughly before deploying it into production.