Async with Expression in Python (77/100 Days of Python)
Asynchronous programming has become an essential part of modern software development. It allows developers to write code that can perform multiple tasks simultaneously, thus improving performance and responsiveness. One of the most useful features of asynchronous programming in Python is the “async with
” statement.
What is async with
Statement in Python?
Async with is a statement that allows developers to use a context manager asynchronously. In Python, a context manager is an object that defines the methods __enter__()
and __exit__()
. The with
statement is used to manage resources such as files, network connections, and locks. async with
statement works in the same way but allows context managers to be used in asynchronous code.
async with
works by defining an asynchronous context manager. An asynchronous context manager is a class that defines the methods aenter()
and aexit()
. When the async with
statement is used, the aenter()
method is called to obtain the context, and the aexit()
method is called to release the context.
How to Define a Custom async with
Context Manager?
Using async with
is straightforward. You can use it just like you use the with
statement. The only difference is that the context manager should be defined as an asynchronous context manager:
import asyncio
class AsyncContextManager:
async def __aenter__(self):
print('Entering the async context')
return self
async def __aexit__(self, exc_type, exc, tb):
print('Exiting the async context')
async def main():
async with AsyncContextManager():
print('Inside the async context')
asyncio.run(main())
This code will output:
Entering the async context
Inside the async context
Exiting the async context
As you can see, the code enters the async context, executes the code inside the async with statement, and then exits the context.
When to Use async with
?
async with
is useful when you want to manage resources asynchronously. For example, you can use async with
to manage network connections, file I/O, and locks. Using async with
can help you avoid blocking the event loop, which can lead to performance issues.
Using async with
for Network Connections
When you are making network requests, it is important to manage connections efficiently. Using async with
can help you manage network connections asynchronously. For example, the aiohttp
library uses async with
to manage HTTP sessions.
import asyncio
import aiohttp
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('https://google.com') as response:
print(await response.text())
asyncio.run(main())
This program will get the contents of the google.com
homepage and print the HTML.
File I/O
When you are reading or writing files, using async with
can help you manage file I/O operations asynchronously. For example, the aiofiles
library uses async with
to manage file I/O.
import asyncio
import aiofiles
async def main():
async with aiofiles.open('file.txt', 'w') as file:
await file.write('Hello, World!')
asyncio.run(main())
This program is almost equivalent to opening a file and writing a Hello, World!
message to it. Yet, in the async context, this can have the benefit of not blocking the main event loop.
Locks
When you need to synchronize access to a shared resource, using locks can help you avoid race conditions. Using async with
can help you manage locks asynchronously. For example, the asyncio
library uses async with
to manage locks:
import asyncio
lock = asyncio.Lock()
async def main():
async with lock:
# Do something here...
asyncio.run(main())
What’s next?
- If you found this story valuable, please consider clapping multiple times (this really helps a lot!)
- Hands-on Practice: Free Python Course
- Full series: 100 Days of Python
- Previous topic: Async Await in Python — Asyncio Deep Dive
- Next topic: Making Requests With asyncio in Python