【Python】aiofilesを用いた非同期ファイル入出力

Pythonで、aiofilesを用いて非同期にファイル入出力する方法を解説します。
目次
aiofilesによる非同期ファイル入出力
非同期処理とは、複数のタスクが協調しあって処理を実行するプログラミングの実装方法です。非同期プログラミングは多くの支持を得ており、Python 3.5でもasyncやawaitといった非同期プログラミング方法が導入されています。
async/awaitを用いたプログラミングの基本は「async/awaitを用いた非同期プログラミングの基本」でまとめているので参考にしてください。
この記事では、非同期処理での効果が期待できるファイル入出力に焦点を当て、aiofilesを用いて非同期にファイル入出力する方法を紹介します。
aiofilesとは
aiofilesは、Pythonの非同期I/O操作を扱うためのライブラリです。Pythonのasyncioモジュールを使って非同期プログラミングを行う際に、ファイル操作を非同期で操作する場合に使用できます。ドキュメントはPyPiのこちらか、GitHubのこちらを参照してください。
非同期プログラミングは、I/Oバウンドな処理を実装するためには非常に有益な実装方法です。I/Oバウンドな処理とは、ファイルへの入出力やネットワーク通信等が該当します。
通常、ファイルの入出力はブロッキング操作となり、大きなファイルを扱うときにプログラムの実行が停止してしまう可能性があります。aiofilesを使用すると、これらの操作を非同期に実行でき、プログラムの他の部分がファイル操作の完了を待つ間に動作を継続することができます。
aiofilesを用いた非同期ファイル入出力の基本
以降では、aiofilesを使って非同期にファイル入出力する方法を紹介していきます。
aiofilesのインストール
aiofilesはサードパーティ製ライブラリのため、インストールが必要です。以下のようにpipでインストールをしてください。
pip install aiofiles
インストールが完了したら非同期にファイルを扱うことが可能です。
aiofilesの基本的な使い方
aiofilesは、標準モジュールのreadやwrite等の非同期版として提供されているため引数などは標準入出力モジュールと同じような使い方になります。標準ファイル入出力については、「ファイル入出力の基本」を参考にしてください。
標準のファイル入出力との主な違いとしては、関数定義にasync defを用いることや関数実行時にwaitキーワードを使用することです。また、async withを使うことでファイルの資源管理を適切に行います。
ファイルの非同期書き込み
aiofilesを使って非同期にファイルを書き込むには以下のようにします。
import asyncio
import aiofiles
async def write_file(filename, content):
"""非同期ファイル書き込み関数"""
async with aiofiles.open(filename, "w", encoding="utf-8") as file:
await file.write(content)
async def main():
# 書込み内容の準備
files_and_contents = {
"sample1.txt": "サンプルファイル1",
"sample2.txt": "サンプルファイル2",
"sample3.txt": "サンプルファイル3",
"sample4.txt": "サンプルファイル4",
"sample5.txt": "サンプルファイル5",
}
# 書き込みタスクを作成
task = [
write_file(filename, content)
for filename, content in files_and_contents.items()
]
# 非同期に複数ファイルを書き込む
await asyncio.gather(*task)
if __name__ == "__main__":
# asyncio.runを使用してメインコルーチンを実行
asyncio.run(main())非同期のファイル入出力では、asyncioとaiofilesをインポートして使用します。上記のプログラムでは、複数のテキストファイルに非同期に書き込みを行っています。
処理の中心となるのはwrite_fileという非同期関数です。この関数は、指定されたファイル名(filename)で新しいファイルを開き、指定した内容(content)を非同期に書き込みを行います。
非同期にファイルを開く場合には、aiofiles.openをasync withで使用し、書き込み時のwriteでは、awaitキーワードを指定することで非同期に書き込み操作を行います。これによりI/O待ち時間を効率的に利用することができます。
メインコルーチン(main)では、書き込むファイル名と内容をfiles_and_contentsという辞書で管理し、非同期のタスクを作成します。これらのタスクはasyncio.gatherを使って非同期に実行され、すべてのファイル書き込みが完了するまで待機します。
上記のようにすることで非同期にファイルの書き込みを行うことが可能です。
ファイルの非同期読み込み
aiofilesを使って非同期にファイルを読み込むには以下のようにします。(前提として、上記で紹介したプログラムを実行するなどしてsample1.txt等のファイルが存在するものとします。)
import asyncio
import aiofiles
async def read_file(filename):
"""非同期ファイル読み込み関数"""
async with aiofiles.open(filename, "r", encoding="utf-8") as file:
content = await file.read()
return content
async def main():
# 読み込みファイルリスト
files = [
"sample1.txt",
"sample2.txt",
"sample3.txt",
"sample4.txt",
"sample5.txt",
]
# 読み込みタスクを作成
task = [read_file(filename) for filename in files]
# 非同期にファイルを読み込む
results = await asyncio.gather(*task)
print(results)
if __name__ == "__main__":
# asyncio.runを使用してメインコルーチンを実行
asyncio.run(main())【実行結果】 ['サンプルファイル1', 'サンプルファイル2', 'サンプルファイル3', 'サンプルファイル4', 'サンプルファイル5']
上記のプログラムでは、複数のテキストファイルを非同期に読み込んでいます。
処理の中心となるのはread_fileという非同期関数です。この関数は、指定されたファイル名(filename)で新しいファイルを開き、読み込んだ内容を返却します。
メインコルーチン(main)では、書き込むファイル名をリストで管理し、非同期の読み込みタスクを作成します。これらのタスクはasyncio.gatherを使って非同期に実行され、すべてのファイルの読み込みが完了すると、結果がresultsに格納されます。結果表示を見てもファイルの内容が読み込めていることが分かるかと思います。
上記のようにすることで非同期にファイルを読み込むことが可能です。
writeやreadを使ってaiofilesの使い方を紹介しましたが、標準モジュールにあるwritelinesやreadlines等も同様に使うことができます。また、バイナリファイルを扱う場合には、モード(mode)に"rb"や"wb"を使うことで対応可能です。
まとめ
Pythonで、aiofilesを用いて非同期にファイル入出力する方法を解説しました。
aiofilesは、Pythonの非同期I/O操作を扱うためのライブラリで、非同期プログラミングを行う際に、ファイル操作を非同期で操作する場合に使用できます。今回は、簡単な非同期入出力の例を用いて基本的な使い方を紹介しました。
非同期プログラミングは、I/Oバウンドな処理を実装するためには非常に有益な実装方法です。ファイルアクセスについても適切に使用することができれば、性能向上できる可能性が高くなります。ぜひ、aiofilesについて使い方を理解していただき、非同期処理をしてみてもらいたいと思います。

