Python中級

中級 Pythonで学ぶ非同期処理|練習問題編

導入

非同期処理は、現代のアプリケーションにおいてパフォーマンスを向上させるための重要な技術です。特に、I/O待機が多いタスクやネットワーク通信を行う場合にその効果を発揮します。この記事では、Pythonを用いた非同期処理の具体的なシチュエーションを紹介し、その実装方法を学びます。

教科書レベルの解説(非同期処理)

重要な概念の整理

非同期処理は、主にイベントループとコルーチンを中心に構成されます。イベントループはタスクの実行を管理し、コルーチンは非同期的に実行される関数です。これにより、他の処理をブロックすることなく、同時に複数のタスクを進行させることが可能になります。

コード例(Python)


import asyncio
import aiohttp

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()

async def main(urls):
    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)
    return results

urls = ['https://api.example.com/data1', 'https://api.example.com/data2']
data = asyncio.run(main(urls))
print(data)

コードの行ごとの解説

  1. import asyncio: asyncioモジュールをインポートします。
  2. import aiohttp: 非同期HTTPリクエストを行うためにaiohttpをインポートします。
  3. async def fetch_data(url):: 指定したURLからデータを非同期に取得する関数を定義します。
  4. async with aiohttp.ClientSession() as session:: aiohttpを使用してHTTPセッションを開始します。
  5. async with session.get(url) as response:: 非同期にGETリクエストを行い、レスポンスを取得します。
  6. return await response.json():: レスポンスをJSON形式で返します。
  7. async def main(urls):: 複数のURLを受け取り、非同期にデータを取得するメイン関数を定義します。
  8. tasks = [fetch_data(url) for url in urls]:: 各URLに対してfetch_data関数を呼び出すタスクを作成します。
  9. results = await asyncio.gather(*tasks):: 全てのタスクを並行して実行し、結果を取得します。
  10. data = asyncio.run(main(urls)):: main関数を実行し、データを取得します。
  11. print(data):: 取得したデータを出力します。

練習問題編

以下の練習問題に取り組んでみましょう。各問題には模範解答と解説が付いています。

  1. 問題1: 上記のコードにエラーハンドリングを追加し、HTTPリクエストが失敗した場合にエラーメッセージを表示するように修正せよ。
  2. 
    async def fetch_data(url):
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as response:
                    response.raise_for_status()
                    return await response.json()
        except Exception as e:
            print(f"Error fetching {url}: {e}")
    

    解説: raise_for_status()を使用して、HTTPエラーが発生した場合に例外を発生させます。これにより、エラーが発生したURLを特定しやすくなります。

  3. 問題2: 非同期処理で取得したデータをCSV形式で保存する関数を実装せよ。
  4. 
    import csv
    
    async def save_to_csv(data, filename):
        with open(filename, mode='w', newline='') as file:
            writer = csv.writer(file)
            writer.writerows(data)
    

    解説: CSVファイルへの書き込みにはcsvモジュールを使用します。非同期処理の結果を受け取り、ファイルに保存します。

  5. 問題3: 同時に実行するタスクの数を制限する方法を説明し、その実装を示せ。
  6. 
    import asyncio
    import aiohttp
    
    async def limited_fetch_data(semaphore, url):
        async with semaphore:
            return await fetch_data(url)
    
    async def main(urls, limit):
        semaphore = asyncio.Semaphore(limit)
        tasks = [limited_fetch_data(semaphore, url) for url in urls]
        results = await asyncio.gather(*tasks)
        return results
    

    解説: asyncio.Semaphoreを使用して、同時に実行するタスクの数を制限します。これにより、リソースの過剰使用を防ぎます。

まとめ

  • 非同期処理は、I/O待機が多いタスクに特に効果的です。
  • エラーハンドリングを適切に行うことで、堅牢なアプリケーションを構築できます。
  • タスクの同時実行数を制限することで、システムリソースの管理が容易になります。