Python の非同期コンテキストマネージャ
Python の非同期プログラミング(asyncio)では、async with 構文を使って非同期コンテキストマネージャを扱います。非同期のリソース管理やセットアップ・クリーンアップ処理に必要です。
非同期コンテキストマネージャとは
通常の with 文は同期的な __enter__() と __exit__() を呼び出しますが、async with は非同期版の __aenter__() と __aexit__() を呼び出します。
同期(with)
enter() と exit() を使用。通常の関数。
非同期(async with)
aenter() と aexit() を使用。コルーチン(async def)。
クラスベースの実装
非同期コンテキストマネージャをクラスで実装する例です。
import asyncio
class AsyncConnection:
def __init__(self, host):
self.host = host
async def __aenter__(self):
print(f"{self.host} に接続中...")
await asyncio.sleep(1) # 接続をシミュレート
print("接続完了")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("切断中...")
await asyncio.sleep(0.5) # 切断をシミュレート
print("切断完了")
async def fetch(self, path):
await asyncio.sleep(0.5)
return f"Data from {self.host}{path}"
async def main():
async with AsyncConnection("example.com") as conn:
data = await conn.fetch("/api/data")
print(data)
asyncio.run(main())@asynccontextmanager デコレータ
contextlib.asynccontextmanager を使うと、非同期ジェネレータから簡単に作成できます。
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def async_timer(label):
start = asyncio.get_event_loop().time()
print(f"{label} 開始")
yield
elapsed = asyncio.get_event_loop().time() - start
print(f"{label} 完了: {elapsed:.2f}秒")
async def main():
async with async_timer("データ取得"):
await asyncio.sleep(1)
asyncio.run(main())aiofiles での非同期ファイル操作
aiofiles ライブラリは非同期ファイル I/O を提供し、async with で使えます。
import asyncio
import aiofiles
async def write_file():
async with aiofiles.open("output.txt", "w") as f:
await f.write("非同期で書き込み")
async def read_file():
async with aiofiles.open("output.txt", "r") as f:
content = await f.read()
print(content)
async def main():
await write_file()
await read_file()
asyncio.run(main())aiohttp での HTTP クライアント
aiohttp は非同期 HTTP クライアントを提供します。
import asyncio
import aiohttp
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("https://example.com")
print(f"取得: {len(html)} 文字")
asyncio.run(main())セッションとレスポンスの両方が非同期コンテキストマネージャです。
asyncio.Lock
非同期ロックも async with で使えます。
import asyncio
lock = asyncio.Lock()
async def critical_section(name):
async with lock:
print(f"{name} がロック取得")
await asyncio.sleep(1)
print(f"{name} がロック解放")
async def main():
await asyncio.gather(
critical_section("タスクA"),
critical_section("タスクB"),
)
asyncio.run(main())AsyncExitStack
動的な非同期リソース管理には AsyncExitStack を使います。
import asyncio
from contextlib import AsyncExitStack
@asynccontextmanager
async def resource(name):
print(f"{name} 確保")
yield name
print(f"{name} 解放")
async def main():
async with AsyncExitStack() as stack:
r1 = await stack.enter_async_context(resource("A"))
r2 = await stack.enter_async_context(resource("B"))
print(f"使用中: {r1}, {r2}")
asyncio.run(main())非同期プログラミングでは、ネットワーク接続やファイル I/O などの I/O バウンドな処理が多くなります。async with を使うことで、これらのリソースを安全に管理できます。












