mcpField
A
async-python-patterns

async-python-patterns

github.com/wshobson開発41.9k18.3k 回インストール2 週間前に更新
リポジトリ

async-python-patterns について

asyncio、並行処理、async/await を使った Python 非同期開発の実践ガイドです。I/O 中心のアプリを開発する人向けに、パターンやテスト、よくある問題を解説します。

主な機能

  1. 01イベントループ、コルーチン、タスク、Future の解説
  2. 02並行実行とタスク管理のパターン
  3. 03エラー、タイムアウト、キャンセル処理
  4. 04同期・非同期の選択指針
  5. 05非同期テストの例

活用シーン

  1. 01並行処理を行う Web API の構築
  2. 02データベースとネットワーク I/O の並行実行
  3. 03並行型 Web スクレイパーの作成
  4. 04WebSocket またはバックグラウンドタスクの開発

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshobson/agents --skill async-python-patterns

インストール後、Claude Code で/async-python-patternsを入力して読み込まれたか確認します。

同梱ファイルreferences/details.md

詳細

非同期 Python パターン

asyncio、並行プログラミングパターン、async/await を用いて、高性能でノンブロッキングなシステムを構築するための非同期 Python アプリケーション実装に関する包括的なガイダンスです。

このスキルを使用する場面

  • 非同期 Web API の構築 (FastAPI、aiohttp、Sanic)
  • 並行 I/O 操作の実装 (データベース、ファイル、ネットワーク)
  • 並行リクエストを行う Web スクレイパーの作成
  • リアルタイムアプリケーションの開発 (WebSocket サーバー、チャットシステム)
  • 複数の独立したタスクの同時処理
  • 非同期通信によるマイクロサービスの構築
  • I/O バウンドなワークロードの最適化
  • 非同期バックグラウンドタスクとキューの実装

同期 vs 非同期の判断ガイド

非同期を採用する前に、それがユースケースに適した選択かどうかを検討してください。

ユースケース推奨されるアプローチ
多数の並行ネットワーク/DB 呼び出しasyncio
CPU バウンドの計算multiprocessing またはスレッドプール
I/O + CPU の混在asyncio.to_thread() で CPU 処理をオフロード
シンプルなスクリプト、接続数が少ない同期 (よりシンプルでデバッグが容易)
高並行性の Web API非同期フレームワーク (FastAPI、aiohttp)

重要なルール: 呼び出しパス内では、完全に同期か完全に非同期のいずれかにしてください。混在させると、隠れたブロッキングと複雑さを生み出します。

中核となる概念

1. イベントループ

イベントループは asyncio の心臓部であり、非同期タスクを管理およびスケジューリングします。

主な特性:

  • シングルスレッドの協調的マルチタスキング
  • コルーチンを実行するようスケジューリング
  • ブロッキングせずに I/O 操作を処理
  • コールバックとフューチャーを管理
2. コルーチン

async def で定義され、一時停止と再開が可能な関数です。

構文:

async def my_coroutine():
    result = await some_async_operation()
    return result
3. タスク

イベントループ上で並行して実行される、スケジューリングされたコルーチンです。

4. フューチャー

非同期操作の最終的な結果を表す低レベルオブジェクトです。

5. 非同期コンテキストマネージャー

適切なクリーンアップのために async with をサポートするリソースです。

6. 非同期イテレーター

非同期データソースを反復処理するために async for をサポートするオブジェクトです。

クイックスタート

import asyncio

async def main():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

# Python 3.7+
asyncio.run(main())

基本的なパターン

パターン 1: 基本的な Async/Await
import asyncio

async def fetch_data(url: str) -> dict:
    """Fetch data from URL asynchronously."""
    await asyncio.sleep(1)  # Simulate I/O
    return {"url": url, "data": "result"}

async def main():
    result = await fetch_data("https://api.example.com")
    print(result)

asyncio.run(main())
パターン 2: gather() による並行実行
import asyncio
from typing import List

async def fetch_user(user_id: int) -> dict:
    """Fetch user data."""
    await asyncio.sleep(0.5)
    return {"id": user_id, "name": f"User {user_id}"}

async def fetch_all_users(user_ids: List[int]) -> List[dict]:
    """Fetch multiple users concurrently."""
    tasks = [fetch_user(uid) for uid in user_ids]
    results = await asyncio.gather(*tasks)
    return results

async def main():
    user_ids = [1, 2, 3, 4, 5]
    users = await fetch_all_users(user_ids)
    print(f"Fetched {len(users)} users")

asyncio.run(main())
パターン 3: タスクの作成と管理
import asyncio

async def background_task(name: str, delay: int):
    """Long-running background task."""
    print(f"{name} started")
    await asyncio.sleep(delay)
    print(f"{name} completed")
    return f"Result from {name}"

async def main():
    # Create tasks
    task1 = asyncio.create_task(background_task("Task 1", 2))
    task2 = asyncio.create_task(background_task("Task 2", 1))

    # Do other work
    print("Main: doing other work")
    await asyncio.sleep(0.5)

    # Wait for tasks
    result1 = await task1
    result2 = await task2

    print(f"Results: {result1}, {result2}")

asyncio.run(main())
パターン 4: 非同期コードでのエラーハンドリング
import asyncio
from typing import List, Optional

async def risky_operation(item_id: int) -> dict:
    """Operation that might fail."""
    await asyncio.sleep(0.1)
    if item_id % 3 == 0:
        raise ValueError(f"Item {item_id} failed")
    return {"id": item_id, "status": "success"}

async def safe_operation(item_id: int) -> Optional[dict]:
    """Wrapper with error handling."""
    try:
        return await risky_operation(item_id)
    except ValueError as e:
        print(f"Error: {e}")
        return None

async def process_items(item_ids: List[int]):
    """Process multiple items with error handling."""
    tasks = [safe_operation(iid) for iid in item_ids]
    results = await asyncio.gather(*tasks, return_exceptions=True)

    # Filter out failures
    successful = [r for r in results if r is not None and not isinstance(r, Exception)]
    failed = [r for r in results if r is None or isinstance(r, Exception)]

    print(f"Success: {len(successful)}, Failed: {len(failed)}")
    return successful

asyncio.run(process_items([1, 2, 3, 4, 5, 6]))
パターン 5: タイムアウト処理
import asyncio

async def slow_operation(delay: int) -> str:
    """Operation that takes time."""
    await asyncio.sleep(delay)
    return f"Completed after {delay}s"

async def with_timeout():
    """Execute operation with timeout."""
    try:
        result = await asyncio.wait_for(slow_operation(5), timeout=2.0)
        print(result)
    except asyncio.TimeoutError:
        print("Operation timed out")

asyncio.run(with_timeout())

詳細な実例とパターン

詳細なセクション (## Advanced Patterns で始まるもの) は references/details.md にあります。上記のナビゲーション概要では不十分な場合に、そのファイルをお読みください。

よくある落とし穴

1. await を忘れる
# Wrong - returns coroutine object, doesn't execute
result = async_function()

# Correct
result = await async_function()
2. イベントループのブロッキング
# Wrong - blocks event loop
import time
async def bad():
    time.sleep(1)  # Blocks!

# Correct
async def good():
    await asyncio.sleep(1)  # Non-blocking
3. キャンセルを処理しない
async def cancelable_task():
    """Task that handles cancellation."""
    try:
        while True:
            await asyncio.sleep(1)
            print("Working...")
    except asyncio.CancelledError:
        print("Task cancelled, cleaning up...")
        # Perform cleanup
        raise  # Re-raise to propagate cancellation
4. 同期コードと非同期コードの混在
# Wrong - can't call async from sync directly
def sync_function():
    result = await async_function()  # SyntaxError!

# Correct
def sync_function():
    result = asyncio.run(async_function())

非同期コードのテスト

import asyncio
import pytest

# Using pytest-asyncio
@pytest.mark.asyncio
async def test_async_function():
    """Test async function."""
    result = await fetch_data("https://api.example.com")
    assert result is not None

@pytest.mark.asyncio
async def test_with_timeout():
    """Test with timeout."""
    with pytest.raises(asyncio.TimeoutError):
        await asyncio.wait_for(slow_operation(5), timeout=1.0)

コメント