7.8 - Coroutines
7.8.1 - Introduction to Coroutines
Coroutines are a powerful feature in Python that allows for efficient asynchronous programming. They enable functions to be paused and resumed, making them ideal for tasks that involve waiting for I/O operations. This guide will provide an understanding of coroutines, their syntax, and practical use cases.
7.8.2 - Understanding async
and await
7.8.2.1 - The async
Keyword
- Definition: The
async
keyword is used to define a coroutine. It modifies a function, indicating that the function is an asynchronous coroutine rather than a normal function. - Usage: Placed before
def
, as inasync def function_name():
. This creates a coroutine function. - Behavior: When called, an
async
function doesn't execute immediately. Instead, it returns a coroutine object that can be executed asynchronously.
7.8.2.2 - The await
Keyword
- Definition:
await
is used to pause the coroutine's execution until the awaited task is complete. It can only be used inside anasync
function. - Usage: Applied before calling a coroutine or any awaitable object, like
await coroutine_name()
. - Behavior: Pauses the execution of the coroutine where it is used and waits for the awaited task to complete. During this pause, other tasks can run.
7.8.2.3 - Interaction and Workflow
async
and await
work together to manage asynchronous tasks. When await
is called in a coroutine, it tells the Python event loop to run other tasks until the awaited task completes, at which point the coroutine resumes.
7.8.3 - Creating and Running Coroutines
To create a coroutine, use the async def
statement. To run a coroutine, you must await it within another coroutine or use an event loop, like asyncio.run()
.
import asyncio
async def my_coroutine():
print("My Coroutine")
# Running the coroutine with an event loop
asyncio.run(my_coroutine())
7.8.4 - Coroutines Case Studies
7.8.4.1 - Asynchronous Web Request
Handling web requests asynchronously is a common use case for coroutines. This example uses aiohttp
to make non-blocking HTTP requests.
import aiohttp
import asyncio
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
html = await fetch_url("http://example.com")
print(html)
asyncio.run(main())
7.8.4.2 - Asynchronous File Operations
Performing file operations asynchronously can improve the efficiency of I/O-bound tasks.
async def read_file_async(file_path):
await asyncio.sleep(1) # Simulating an I/O-bound task
with open(file_path, 'r') as file:
return file.read()
async def main():
content = await read_file_async('example.txt')
print(content)
asyncio.run(main())
7.8.4.3 - Producer-Consumer Using Asyncio
This demonstrates the producer-consumer pattern in asynchronous programming.
async def producer(queue):
for i in range(10):
await queue.put(i)
print(f'Produced {i}')
await asyncio.sleep(1)
async def consumer(queue):
while True:
item = await queue.get()
print(f'Consumed {item}')
queue.task_done()
async def main():
queue = asyncio.Queue()
producer_coroutine = producer(queue)
consumer_coroutine = consumer(queue)
await asyncio.gather(producer_coroutine, consumer_coroutine)
asyncio.run(main())
7.8.4.4 - Asynchronous Timer
Creating a simple asynchronous timer to execute tasks at regular intervals.
async def timer(interval, task):
while True:
await asyncio.sleep(interval)
await task()
async def print_time():
print(f"Task executed at {time.strftime('%X')}")
async def main():
await timer(5, print_time) # Execute `print_time` every 5 seconds
asyncio.run(main())
7.8.4.5 - Chat Server with Asyncio
Building an asynchronous chat server to handle multiple client connections.
import asyncio
async def handle_client(reader, writer):
while True:
data = await reader.read(100)
if data:
message = data.decode()
print(f"Received: {message}")
writer.write(data)
await writer.drain()
else:
print("Closing connection")
writer.close()
break
async def main():
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8888)
async with server:
await server.serve_forever()
asyncio.run(main())