withで安全に扱う
書いたはずなのに、ファイルが空だった
with を使わずに書くと、こうなります。
Python
f = open("kakeibo.txt", "w", encoding="utf-8")
f.write("りんご,120,食費")このまま先へ進むと、ファイルを開いてみても中身が空のことがあります。write で渡した文字列は、いったん手前にためられてからまとめてファイルへ送られるからです。送り切るのは閉じるときです。
閉じるには f.close() を呼びます。ただ、この 1 行は書き忘れます。しかも忘れても、その場では何のエラーも出ません。
with を使うと、閉じる仕事を Python 側が引き受けます。
Python
with open("kakeibo.txt", "w", encoding="utf-8") as f:
f.write("りんご,120,食費")ブロックを抜けた時点で閉じられ、中身は確実にファイルへ届いています。close の行は書きません。書いても動きますが、意味のない 1 行になります。
途中で例外が出ると、close にたどり着けない
with の値打ちは、書き忘れを防ぐことだけではありません。
f.close() を律儀に書いたとしても、その手前の行で例外が起きれば close は実行されないまま関数を抜けます。ファイルは開きっぱなしになり、書いた内容も届きません。しかも症状はあとから出るので、原因にたどり着くのに時間がかかります。
with はブロックを抜けるときに必ず閉じます。途中で例外が起きた場合も、閉じてから例外を投げ直します。うまくいった道と失敗した道の、どちらを通っても片付けは済んでいます。
ブロックを抜けたあとの f は使えない
with のブロックは、インデントされている範囲です。ここがそのままファイルの寿命になります。
Python
with open("kakeibo.txt", "r", encoding="utf-8") as f:
text = f.read()
lines = text.splitlines()read はブロックの中、splitlines は外です。ブロックを抜けたあとに f.read() を呼ぶと ValueError になります。閉じたファイルは読めません。
一方、text に入れた文字列はそのまま使えます。閉じられるのはファイルであって、変数の中身ではありません。ファイルとのやり取りだけを中に置き、計算や整形は外に出す。この切り分けがそのまま読みやすさになります。
やってみよう
関数 save_text(path, text) を実装してください。with を使って path に text を書き出し、そのあともう一度 with で同じファイルを読み込み、読み戻した文字列を return します。
書きと読みで with は 2 回に分けます。書きモードで開いたファイルからは読めないので、1 つにまとめることはできません。open には encoding="utf-8" を指定し、close は書かずに with に任せてください。text が空文字のときは空文字が返れば正解です。
要件
- 関数名は save_text で、引数は path と text の 2 つ
- 書き込みと読み込みの両方で with 文を使う
- close は書かず、with に閉じる処理を任せる
- 読み戻した文字列を return する
- open には encoding="utf-8" を指定する