[前回] 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ストリームのクラス階層とそれぞれの違いを理解しました。
次回も続きます。お楽しみに。