π§ Introduction
In modern Python programming, writing efficient and scalable code often means dealing with asynchronous operations. Whether youβre building a web server, scraping websites, or handling thousands of API requests, coroutines are the secret weapon behind Pythonβs asynchronous magic.
This article will walk you through:
- What coroutines are in Python
- How they differ from functions and generators
- How to use
async
/await
syntax - Real-world examples and best practices
π What Is a Coroutine?
A coroutine is a special kind of function that can pause and resume its execution. In Python, coroutines are used in asynchronous programming, which allows tasks to be performed without blocking the entire program.
A coroutine is declared with
async def
and is executed usingawait
.
Coroutines allow your code to remain responsive and efficient, especially during I/O-bound tasks like network requests or file operations.
β Why Use Coroutines?
- π Non-blocking execution
- π Concurrency with a single thread
- π Improved performance for I/O tasks
- π Lower resource usage compared to threads/processes
π§ͺ Coroutine Basics: async
and await
Hereβs how you define and use a coroutine:
import asyncio
async def greet():
print("Hello")
await asyncio.sleep(1)
print("World")
# Running the coroutine
asyncio.run(greet())
Explanation:
async def greet()
defines an asynchronous coroutine.await asyncio.sleep(1)
pauses the coroutine for 1 second without blocking other coroutines.
π Running Multiple Coroutines Concurrently
The real power of coroutines is visible when running multiple tasks together.
import asyncio
async def download_file(file_id):
print(f"Downloading {file_id}")
await asyncio.sleep(2)
print(f"Finished {file_id}")
async def main():
tasks = [
download_file(1),
download_file(2),
download_file(3)
]
await asyncio.gather(*tasks)
asyncio.run(main())
This runs all three download_file
coroutines concurrently, using the same thread and without blocking.
π§΅ Coroutines vs Threads
Feature | Coroutines (asyncio ) | Threads |
---|---|---|
Threading Required | β No | β Yes |
Memory Footprint | π’ Low | π΄ High (1 thread per task) |
Suitable For | I/O-bound tasks | I/O and some CPU tasks |
True Parallelism | β No (single-threaded) | β Limited by GIL in CPython |
Syntax | async / await | threading.Thread |
β οΈ Things You Can and Canβt await
You can await
any awaitable object. These include:
- Coroutines (declared with
async def
) asyncio.sleep
,asyncio.open_connection
, etc.- Custom objects that implement
__await__()
You canβt await
normal functions or blocking I/O (like time.sleep()
).
β Bad:
await time.sleep(1)
β Good:
await asyncio.sleep(1)
π Real-World Use Cases
1. Web Servers (FastAPI, AIOHTTP)
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/delay")
async def delayed_response():
await asyncio.sleep(2)
return {"message": "Done!"}
2. Web Scraping
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = ["https://example.com", "https://python.org"]
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
π§ Best Practices
- β
Always use
asyncio.run()
to start the main coroutine (Python 3.7+). - β
Use
await
only insideasync def
. - β Avoid blocking functions like
time.sleep()
orrequests.get()
inside coroutines β they will freeze the event loop. - β
Use third-party async libraries like
aiohttp
for non-blocking I/O.
π§© Advanced: Creating Your Own Coroutine
You can create a coroutine manually using a generator-style syntax (not recommended unless youβre building libraries):
def manual_coroutine():
yield
# But prefer using:
async def async_coroutine():
await asyncio.sleep(1)
π Conclusion
Coroutines are a cornerstone of asynchronous programming in Python. They let you write non-blocking, concurrent code that is clean, readable, and performant β especially for I/O-bound tasks.
By mastering async
and await
, you unlock the full potential of modern Python for building APIs, bots, scrapers, and event-driven systems.
Leave a Reply