But first, let me ask you a question: have you ever wished your code could pause and resume execution at certain points? Well, my friend, you’re in luck because that’s exactly what coroutines do!
So, what are these magical creatures called coroutines anyway? According to Wikipedia (because who doesn’t trust the internet), “coroutines are computer program components that generalize subroutines for nonpreemptive multitasking by allowing multiple entry points for suspending and resuming execution at certain locations.” In simpler terms, they’re functions whose execution you can pause.
Now, if this sounds familiar to you, it might be because coroutines are actually just fancy generators (which we all know and love). But with some extra support from the language itself, they become even more powerful! Let me explain.
First off, how to create a coroutine in Python 3.5+. You can do this by simply adding an “async” keyword before your function definition:
# Import the asyncio library to use its functions
import asyncio
# Define a coroutine function using the "async" keyword
async def my_coroutine():
print("Hello, world!")
# Use the "await" keyword to pause the coroutine for one second
await asyncio.sleep(1)
print("Goodbye, cruel world.")
# Create an event loop to run the coroutine
loop = asyncio.get_event_loop()
# Assign the coroutine to a variable
coro = my_coroutine()
# Run the coroutine using the event loop
loop.run_until_complete(coro)
# Output:
# Hello, world!
# (pause for one second)
# Goodbye, cruel world.
# Explanation:
# The "async" keyword before the function definition indicates that it is a coroutine.
# The "await" keyword is used to pause the coroutine and wait for a result before continuing.
# The asyncio.sleep() function is used to pause the coroutine for a specified amount of time.
# The event loop is responsible for running the coroutine and managing its execution.
# The run_until_complete() function is used to run the coroutine until it is complete.
# This allows the coroutine to print "Hello, world!" and then pause for one second before printing "Goodbye, cruel world."
Notice how we’re calling `asyncio.sleep()` inside the coroutine? That’s what makes it pause! You can also create a generator-based coroutine by adding the “types.coroutine” decorator to your function:
# Importing necessary modules
import asyncio
import datetime
from typing import Coroutine
from datetime import timedelta
# Defining a generator-based coroutine function
@types.coroutine
def my_generator():
# Using yield to pause at each yield statement
yield from [1, 2, 3]
# Defining a function to wait for a specified number of seconds
def wait(seconds):
# Getting current time
now = datetime.datetime.now()
# Calculating the time to wait until
wait_until = now + timedelta(seconds=seconds)
# Using yield to pause until the specified time
actual = yield wait_until
# Returning the actual time waited
return actual
# Converting the generator to a coroutine
coro: Coroutine[None, None, int] = asyncio.coroutine(wait("10 seconds"))
# Getting the event loop
loop = asyncio.get_event_loop()
# Running the coroutine until it is complete
result = loop.run_until_complete(coro)
# Printing the result
print(result)
In this example, we’re creating a generator that yields three numbers and then pauses at each yield statement (just like with regular generators). But instead of returning the values immediately, we’re wrapping it in an `asyncio.coroutine()` function to convert it into a coroutine. This allows us to use await expressions inside our code!
So what exactly is an await expression? It’s basically yield from but with restrictions you can only wait for objects that define the __await__() method (which returns an iterator). Here’s an example:
# Function to convert it into a coroutine. This allows us to use await expressions inside our code!
async def my_coroutine(): # added 'async' keyword to indicate that this function is a coroutine
print("Hello, world!")
await some_function() # wait for another coroutine or object with __await__() method
print("Goodbye, cruel world.")
# Async function that returns an Awaitable object
async def some_function():
await asyncio.sleep(1) # pause for one second
return 42
# Main function to run the coroutine
async def main():
await my_coroutine() # await the completion of the coroutine
# Create an event loop and run the main function
loop = asyncio.get_event_loop()
loop.run_until_complete(main()) # run the main function until it is complete
loop.close() # close the event loop
# Output:
# Hello, world!
# (pause for 1 second)
# Goodbye, cruel world.
# Explanation:
# - The 'async' keyword is added to the 'my_coroutine' function to indicate that it is a coroutine.
# - The 'await' keyword is used to wait for the completion of another coroutine or an object with the '__await__()' method.
# - The 'some_function' function is an async function that returns an Awaitable object.
# - The 'await' keyword is used to pause the execution of the coroutine until the Awaitable object is complete.
# - The 'asyncio.sleep()' function is used to pause the execution of the coroutine for a specified amount of time.
# - The 'main' function is created to run the coroutine.
# - The event loop is created and the 'main' function is run until it is complete.
# - The event loop is then closed.
In this example, we’re calling `some_function()` inside our coroutine using the “await” keyword. This allows us to wait for its completion before continuing with our code!
And that’s it, You now know everything you need to know about Python coroutines (or at least enough to get started). They’re a powerful tool in your programming arsenal and can help you write more efficient and concurrent code. So go out there and start experimenting with them who knows what kind of magic you’ll create!