中学英語808712 views
LaTeX957300 views
高校国語785655 views
世界の国560595 views
小学算数1194618 views
高校倫理1433119 views
雑学1472593 views
中学数学621382 views
英語607877 views
中学理科1626207 views
Help
Tools

English

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 を使うことで、これらのリソースを安全に管理できます。