[前回] Python I/Oの再考(2): ioモジュールの高レベルインターフェース
はじめに
前回は、io
モジュールの高レベルインターフェースを理解しました。
今回は、io
モジュールのクラス群です。
抽象基底クラス(ABC, Abstract Base Class)とは
- インターフェースを定義する方法を提供
- 具象クラスの実装に役立つデフォルトの実装を提供
- 仮想サブクラスを導入可能
- 他のクラスの継承ではないが、
isinstance()
、issubclass()
により認識可能
- 他のクラスの継承ではないが、
io
モジュールが提供する抽象基底クラス(ABC)と階層
-
IOBase
: I/O階層の最上位、全I/Oクラスのベースクラス-
RawIOBase
: IOBaseを拡張、Rawバイナリストリーム操作用 -
BufferedIOBase
: IOBaseを拡張、バッファリングバイナリストリーム操作用 -
TextIOBase
: IOBaseを拡張、テキストストリーム操作用
-
抽象基底クラスと具象クラスの関係
- 抽象基底クラスABC(ストリームのカテゴリ分類に使用)
- 具象クラス1(標準のストリームを実装)
- 具象クラス2(標準のストリームを実装)
- ... ...
I/Oストリームのクラス階層
-
IOBase
: ストリームの基本インターフェースを定義、デフォルトは読み込み/書き込み/シーク不能-
RawIOBase
: IOBaseを継承、Rawバイナリストリームでbytes
の読み書きを行う-
FileIO
: RawIOBaseを継承、ファイルシステムのファイルへのインターフェースを提供
-
-
BufferedIOBase
: IOBaseを継承、バッファリングバイナリストリームで、RawIOBaseのRawバイナリストリームへの高レベルアクセスを提供-
BufferedReader
: BufferedIOBaseを継承、読み込み可能かつシーク不能なRawバイナリストリームへのバッファリングインターフェースを提供 -
BufferedWriter
: BufferedIOBaseを継承、書き込み可能かつシーク不能なRawバイナリストリームへのバッファリングインターフェースを提供 -
BufferedRandom
: BufferedReaderとBufferedWriterを継承、シーク可能なRawバイナリストリームへのバッファリングインターフェースを提供 -
BufferedRWPair
: BufferedIOBaseを継承、シーク不能なRawバイナリストリームへの二つのバッファリングインターフェースを提供(それぞれ読み込みと書き込み) -
BytesIO
: BufferedIOBaseを継承、バイナリストリームで、インメモリbytesバッファを使用
-
-
TextIOBase
: IOBaseを継承、テキストストリームのベースクラス-
TextIOWrapper
: TextIOBaseを継承、テキストストリームでBufferedIOBaseのバッファリングバイナリストリームへの高レベルアクセスを提供 -
StringIO
: TextIOBaseを継承、テキストストリームで、インメモリテキストバッファを使用
-
-
FileIO
クラスとBufferedReader
クラスの違いを検証
FileIO
は、RawIOBase
(バッファリングなし)を継承し、
BufferedReader
は、BufferedIOBase
(バッファリングあり)を継承します。
何がどう違うか、AlmaLinux環境で検証してみます。
FileIOクラスによるファイル読み込みを確認
- ファイル
test_big_file
を作成
ファイル中身は任意。
ファイルサイズは6020バイトとなっています。
$ ls -l test_big_file
-rw-rw-r-- 1 zhao zhao 6020 8月 21 11:42 test_big_file
- Pythonスクリプト作成
- ファイルを、バッファリングを無効にし、バイナリモードで開く
- 5120バイトを読み込む
with open("test_big_file", "rb", buffering=0) as f:
print(type(f))
data = f.read(5120)
print(type(data))
print(len(data))
- Pythonスクリプトを実行
- straceコマンドでシステムコールを採取
$ strace -f -o strace_fileio.log python test_fileio.py
- straceログ
strace_fileio.log
を開き-
test_big_file
ファイルに対するシステムコールを確認
-
openat(AT_FDCWD, "test_big_file", O_RDONLY|O_CLOEXEC) = 3
read(3, "\346\244\234\350\250\274\346"..., 5120) = 5120
システムコールread()
は1回のみ実行される。
ソースコードのf.read()メソッド
で指定した5120バイトを、
バイト単位でびったり読み込んでくれています。
FileIOクラスの読み込み単位は、1バイトであることがわかります。
BufferedReaderクラスによるファイル読み込みを確認
- Pythonスクリプト作成
- ファイルをバイナリモードで開く
- 5120バイトを読み込む
with open("test_big_file", "rb") as f:
print(type(f))
data = f.read(5120)
print(type(data))
print(len(data))
- Pythonスクリプトを実行
- straceコマンドでシステムコールを採取
$ strace -f -o strace_buffered-reader.log python test_buffered-reader.py
- straceログ
strace_buffered-reader.log
を開き-
test_big_file
ファイルに対するシステムコールを確認
-
openat(AT_FDCWD, "test_big_file", O_RDONLY|O_CLOEXEC) = 3
read(3, "\346\244\234\350\250\274\346"..., 4096) = 4096
read(3, "\350\250\274\346\244\234\350"..., 4096) = 1924
close(3)
システムコールread()
が2回実行されました。
1回目は、4096バイト(バッファサイズ分)を読み込み、
2回目は、1924バイトを読み込んでいます。
合わせると、4096+1924=6020バイト
あれ、ソースコードのf.read()メソッド
で指定した5120バイトではなく、
ファイルサイズ6020バイト分をすべて読み込んでいます。
つまり、BufferedReaderクラスの読み込み単位は、
バッファサイズ(4096バイト)であることがわかります。
おわりに
I/Oストリームのクラス階層とそれぞれの違いを理解しました。
次回も続きます。お楽しみに。