中学理科1626207 views
英語607877 views
小学算数1194618 views
いろは2986023 views
高校生物549842 views
小学社会308636 views
LaTeX957300 views
Computer365120 views
高校化学2913383 views
小学理科717236 views
Help
Tools

English

Python で例外発生時にファイルはどうなるか:with 文は完璧か

Python でファイルを開いているときに例外が発生したら、そのファイルはどうなるのか。メモリリークは起きるのか。with 文を使えば本当に安全なのか。これらの疑問を整理する。

例外発生時にファイルはどうなるか

まず、with 文を使わない場合を見てみよう。

f = open("data.txt", "w")
f.write("hello")
raise Exception("何かのエラー")  # ここで例外発生
f.close()  # 実行されない

この場合、f.close() は実行されない。ファイルオブジェクトは閉じられないまま残る。

CPython の場合

参照カウントがゼロになった時点でガベージコレクタがファイルを閉じる。ただし「いつ」閉じられるかは保証されない

PyPy / Jython の場合

参照カウント方式ではないため、GC が走るまでファイルは開いたまま。長時間閉じられない可能性がある

メモリリークは起きるか

厳密に言えば「メモリリーク」ではなく「リソースリーク」が起きる。

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

OS には同時に開けるファイル数の上限がある。閉じ忘れが続くと「Too many open files」エラーになる。

バッファの未フラッシュ

書き込みモードで開いたファイルは、close() されるまでバッファの内容がディスクに書き込まれない可能性がある。データ消失の原因になる。

メモリ自体は回収される

ファイルオブジェクト自体のメモリは GC により回収される。ただし OS レベルのリソースは別問題。

with 文は完璧か

with 文を使えば、例外が発生しても __exit__ メソッドが呼ばれ、ファイルは確実に閉じられる。

with open("data.txt", "w") as f:
    f.write("hello")
    raise Exception("何かのエラー")
    # ここで例外が発生しても、with を抜けるときに f.close() が呼ばれる

これは try-finally と等価だ。

f = open("data.txt", "w")
try:
    f.write("hello")
    raise Exception("何かのエラー")
finally:
    f.close()  # 例外が発生しても必ず実行される

では with 文は完璧か。ほぼ完璧だが、いくつかの落とし穴がある。

with 文の落とし穴

open() 自体が失敗するケース

with open(...) の open() 呼び出し自体で例外が発生した場合、ファイルはそもそも開かれていないので問題ない。ただし複数ファイルを開く場合は注意が必要。

KeyboardInterrupt や SystemExit

通常の例外と同様に __exit__ は呼ばれる。ただし SIGKILL でプロセスが強制終了された場合は何も実行されない。

__exit__ 内での例外

__exit__ 自体が例外を発生させると、リソースが正しく解放されない可能性がある。ただしファイルオブジェクトの __exit__ は単純な close() なので、これが失敗することは稀。

複数ファイルを開く場合

複数ファイルを扱うときは、2 つ目の open() で例外が発生すると 1 つ目のファイルが閉じられない問題がある。

# 危険なパターン
with open("file1.txt") as f1:
    with open("file2.txt") as f2:  # ここで失敗すると f1 は閉じられる(OK)
        pass

# Python 3.1+ ではまとめて書ける
with open("file1.txt") as f1, open("file2.txt") as f2:
    pass
# この書き方なら、f2 の open() が失敗しても f1 は閉じられる

ただし、上記のネストした with 文でも実際には問題ない。外側の with 文の __exit__ が呼ばれるからだ。

del に頼るべきではない理由

「どうせ GC がファイルを閉じてくれる」と考えるのは危険だ。

GC のタイミングは予測不能

循環参照があると回収が遅れる

PyPy 等では特に遅延が顕著

プロセス終了時まで閉じられない可能性

CPython でも、関数を抜けた直後にファイルが閉じられる保証はない。明示的に閉じるか、with 文を使うのが正解だ。

結論

with 文を使えば十分安全

通常のプログラミングでは with 文で十分。例外が発生してもファイルは閉じられる。

完璧ではないが実用上は問題ない

SIGKILL による強制終了など、どうしようもないケースを除けば with 文は信頼できる。そもそも SIGKILL では何をやっても無駄。

with 文を使わない理由はない。ファイル操作では常に with 文を使い、リソース管理を Python に任せるのがベストプラクティス。