例外の種類
いちばん最初の起動では、まだファイルが無い
保存機能を入れたら、起動時にまず保存ファイルを読み込みたくなります。ところが 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 のような広い書き方は使わないでください。
補助関数の判定は、既に学んだ is None を not で反転した not (initial_text is None) です。空文字も「渡された」として扱います。たとえば、ノートのメモ欄で確かめてみます。
Python
for memo in [None, "", "青い表紙"]:
if not (memo is None):
print("メモの指定あり", memo)None のときだけ表示されず、空文字と "青い表紙" のときは表示されます。空文字は「空にしたい」という指定にも使えます。補助関数も同じ判定なので、空文字を渡すとファイルを空の内容で書き直します。if initial_text では空文字も省略と同じ扱いになってしまいます。
要件
- 関数名は load_or_empty で、引数は path と initial_text の 2 つ。initial_text の既定値は None
- ひな形の _prepare_file(path, initial_text) はそのまま使う。None なら準備せず、空文字なら空のファイルを用意する
- 読み込みは try で囲み、splitlines で行のリストにして返す
- except FileNotFoundError で受け止めて空リストを返す
- except Exception のように広く捕まえる書き方はしない