つくる:保存機能
保存と読み戻しは、必ず対で作る
この章では、読み込み、書き込み、with、CSV、例外の受け止めを扱ってきました。最後にそれらを 1 本につなぎます。
作るのは save_and_load(path, records) です。渡された辞書のリストを保存し、そのファイルを読み戻して、元と同じ形の辞書のリスト を返します。入れたものと同じものが出てくるだけの地味な関数ですが、これが成り立つということは、アプリを閉じても記録が失われないということです。
既習の保存処理は補助関数 _save_records(path, records) として渡します。1 件を "品名,金額,カテゴリ" の行にし、改行でつないで with で書き出します。読み込みも _read_text(path) が with でファイルを開き、中身の文字列を返します。どちらの補助関数も完成済みです。
今回書くのは、保存と読み込みの補助関数を順に呼び、読み戻した文字列を辞書のリストへ復元する部分です。splitlines で行に分け、1 行ずつ split(",") で 3 つに分けます。分かれた値は、タプルアンパックでそのまま 3 つの変数に受けられます。次の例は、在庫ラベルの色・個数・サイズを取り出すものです。
片方だけ先に完成させると、書いた形式が読めないことに後から気づいて作り直しになります。1 件だけの記録で書いて読んで、往復できることを確かめてから件数を増やしてください。
ファイルから読んだ値は、すべて文字列
いちばん引っかかるのがここです。
Python
color, count, size = "青,4,小".split(",")
print(count + 2)TypeError になります。count に入っているのは "4" という文字列で、数値ではありません。ファイルには文字しか入っていないので、読み戻した時点ではすべて str です。
計算する前に int を通します。
Python
count = int(count)
print(count + 2) # 6演習でも辞書に入れる前に金額を整数へ戻します。忘れると、合計を計算するところで "120" + "80" が "12080" になる、という第 2 章で見た事故が起きます。見た目は似ているのでテストが落ちるまで気づきません。
書くときに str、読むときに int。この対称性が崩れると、型がじわじわ混ざって原因の分かりにくいバグになります。
0 件のときが、いちばん壊れやすい
記録が 0 件のときを先に確かめます。書き出す文字列は空になり、中身が空のファイルができます。読み戻すと read は "" を返します。
Python
print("".splitlines())
print("".split("\n"))[] と [''] です。splitlines なら for が 1 度も回らず、場合分けを書かずに空リストが返ります。split("\n") を使っていた場合は空文字が 1 件あることになり、それをカンマで 3 つに分けようとして ValueError で落ちます。
with を 1 つにまとめようとして詰まるのも定番です。書きモードで開いたファイルからは読めないので、書きと読みで 2 回に分けます。
やってみよう
関数 save_and_load(path, records) を実装してください。records を "品名,金額,カテゴリ" の行にして path に保存し、そのファイルを読み戻して辞書のリストに復元して返します。
キーは name、price、category の 3 つで、保存の順もこのとおりです。金額は保存時に文字列、復元時に int を通します。records が空リストのときは空リスト [] が返れば正解です。ファイルの読み書きは、with を使う完成済みの補助関数に任せてください。ひな形には復元先の loaded と最後の return もあります。コメントの位置に補助関数の呼び出しと復元処理を書きます。品名とカテゴリにカンマや改行は含まれず、金額は 0 以上の整数として渡されます。
要件
- 関数名は save_and_load で、引数は path と records の 2 つ
- 完成済みの _save_records(path, records) を呼び、続いて _read_text(path) の戻り値を受け取る
- 補助関数が with を使って保存と読み込みを行う。補助関数はそのまま使う
- 読み戻した文字列を splitlines で行に分け、各行を split でカンマごとに分けて name・price・category の辞書を loaded に追加する
- 金額は int に戻す。records が空リストのときは空リストを返す
- 品名とカテゴリにカンマや改行は含まれず、金額は 0 以上の整数。記録の順番を保つ