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 を抜けたときにファイルもクローズされます。
これちょっとイマイチ
— Atsuo Ishimoto (@atsuoishimoto) 2019年4月29日
なんで with open(...) as f: 〜 ってするかって言うと、GCに頼らず、ブロックを抜けたときに確実にcloseするためだけど、これだとファイルを最後まで読まずに抜けたときに GC が動いてジェネレータが開放されるまでファイルがcloseされないのでwithの意味がない
俺だったらこんな感じにする
— Atsuo Ishimoto (@atsuoishimoto) 2019年4月29日
import contextlib
@contextlib.contextmanager
def load():
with open() as f:
yield csv.reader(f)
with load() as rows:
for row in rows:
...
勉強になりました!
他のオススメ記事
Python3.8のPEP572 (Assignment Expression) とリスト内包表記でフィボナッチ数列を作って遊んだ記録 - Make組ブログblog.hirokiky.org