Make組ブログ

Python、Webサービスや製品開発、ライブラリー開発についてhirokikyが書きます

PythonでValueError: I/O operation on closed fileを避けるためにwith open() return せずにコンテキストマネージャーにする

Pythonでファイルを with open してファイルを読む前に return しちゃうとファイルがクローズしてしまいます。 ValueError: I/O operation on closed file エラーが発生します。

def load():
    with open(...) as f:
        return csv.reader(f)


>>> for row in load():
...     print(row)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

こういうときは contextlib.contextmanager を使ってコンテキストマネージャーにすると、ファイルを全部読み込むまでちゃんとファイルが閉じられません。

import contextlib


@contextlib.contextmanager
def load():
    with open() as f:
        yield csv.reader(f)


>>> with load() as rows:
...     for row in rows:
...         print(row)

yield from を使って書くとループの途中で抜けたときにジェネレーターが生き続けるんで、ファイルがクローズされなくなります。

def load():
    with open(...) as f:
        yield from csv.reader(f)

そうするとGCがジェネレーターを消すまでファイルが開きっぱなしになります。 with load() で書けるようにしとけば、その with を抜けたときにファイルもクローズされます。

勉強になりました!