ファイルを開いたまま閉じ忘れる問題と with 文の重要性

ファイルを開いた後に close() を呼び忘れると、リソースリークやデータ損失を引き起こす可能性がある。

close() を忘れるとどうなるか

# ❌ アンチパターン
f = open('data.txt', 'w')
f.write('some data')
# close() を忘れている

この場合、以下の問題が発生する可能性がある。

書き込んだデータがディスクに反映されない(バッファに残ったまま)
ファイルディスクリプタが枯渇する
他のプロセスがファイルにアクセスできない(Windows で顕著)
プログラムが異常終了するとデータが消える

例外が発生すると close() されない

close() を書いていても、その前に例外が発生すると呼ばれない。

# ❌ 例外が発生すると close() されない
f = open('data.txt', 'w')
process_data(f)  # ここで例外が発生すると…
f.close()  # この行は実行されない

try-finally で対処できるが、冗長になる。

# △ 動くが冗長
f = open('data.txt', 'w')
try:
    process_data(f)
finally:
    f.close()

with 文を使う

with 文を使えば、ブロックを抜けるときに自動的にファイルが閉じられる。例外が発生しても確実に閉じられる。

# ✅ 正しい方法
with open('data.txt', 'w') as f:
    f.write('some data')
# ここで自動的に close() される

例外が発生した場合も安全だ。

with open('data.txt', 'w') as f:
    process_data(f)  # 例外が発生しても
# 確実に close() される

複数ファイルを開く

複数のファイルを同時に開く場合も with 文を使う。

# ✅ 複数ファイルを同時に開く
with open('input.txt', 'r') as fin, open('output.txt', 'w') as fout:
    for line in fin:
        fout.write(line.upper())

Python 3.10 以降では、括弧で囲んで複数行に分けられる。

with (
    open('input.txt', 'r') as fin,
    open('output.txt', 'w') as fout,
    open('log.txt', 'a') as flog
):
    # 処理
    pass

pathlib でも with を使う

pathlibopen() メソッドでも同様に with 文を使うべきだ。

from pathlib import Path

path = Path('data.txt')

with path.open('w') as f:
    f.write('some data')

ただし、単純な読み書きなら read_text()write_text() がさらに簡潔だ。

from pathlib import Path

path = Path('data.txt')

# 読み込み
content = path.read_text()

# 書き込み
path.write_text('some data')

これらのメソッドは内部で適切にファイルを閉じるため、with 文を書く必要がない。

ファイルディスクリプタの枯渇

close() し忘れたファイルが蓄積すると、OS のファイルディスクリプタ上限に達してエラーになる。

# ❌ ファイルディスクリプタが枯渇する例
files = []
for i in range(10000):
    f = open(f'file_{i}.txt', 'w')
    files.append(f)  # close() されないファイルが蓄積
# OSError: [Errno 24] Too many open files

with 文を使えばこの問題は発生しない。