例外の種類
いちばん最初の起動では、まだファイルが無い
保存機能を入れたら、起動時にまず保存ファイルを読み込みたくなります。ところが 1 回目の起動では、そのファイルはまだ存在しません。
Python
with open("kakeibo.csv", "r", encoding="utf-8") as f:
text = f.read()この行は FileNotFoundError で止まります。メッセージは No such file or directory で、探しにいったパスまで教えてくれます。前回の ValueError とは別の名前です。例外の名前は、何が起きたのかを説明する短いラベルになっていて、読めば直し方の見当が付きます。
ここで欲しい動きは「無いなら、0 件として始める」です。止めるのではなく、既定の値を用意して先へ進みます。
Python
try:
with open("settings.txt", "r", encoding="utf-8") as f:
theme = f.read()
except FileNotFoundError:
theme = "light"設定ファイルがあればその中身を、無ければ既定の "light" を使う形です。try の中に with を丸ごと入れているのがポイントです。例外が起きるのは open の時点なので、open が try の中に入っていなければ受け止められません。with の外側だけを囲んでも意味がありません。
広く捕まえるほど、壊れ方に気づけなくなる
except Exception と書けば、ほとんどの例外を一手に受け止められます。楽に見えますが、これは避けます。
たとえばパスを書き間違えてディレクトリを指してしまうと、起きるのは IsADirectoryError です。これを「ファイルが無い」と同じ扱いにして空の結果を返すと、設定ミスに気づけないまま、記録が消えたように見えます。
受け止める種類を絞っておけば、想定していない例外はそのまま表に出ます。表に出れば直せます。広く捕まえるほどプログラムは静かになりますが、静かなプログラムは壊れていても気づけません。
種類を取り違えるのもよくある失敗です。except ValueError では FileNotFoundError を受け止められず、例外はそのまま素通りして止まります。受け止めたあとに return を書き忘れるのも定番で、関数は None を返し、呼び出した側が for で回した瞬間に別のエラーになります。空リストと None は別ものです。
やってみよう
関数 load_or_empty(path, initial_text=None) を実装してください。initial_text が None でなければ、まず path にその内容を書き出します。そのうえで path を読み込み、splitlines で行のリストにして返します。
ファイルが存在しないときは FileNotFoundError を受け止めて空リスト [] を返します。受け止める種類はこれだけにして、except Exception のような広い書き方は使わないでください。
省略されたかどうかの判定には initial_text is not None を使います。空文字も「渡された」として扱いたいので、if initial_text: と書くと空文字が省略と同じ扱いになってしまいます。
要件
- 関数名は load_or_empty で、引数は path と initial_text の 2 つ。initial_text の既定値は None
- initial_text が None でなければ、先に path へ書き出す
- 読み込みは try で囲み、splitlines で行のリストにして返す
- except FileNotFoundError で受け止めて空リストを返す
- except Exception のように広く捕まえる書き方はしない