つくる:保存機能

このレッスンで作るもの — 第 8 章の総仕上げ。記録をファイルに保存し、読み戻して辞書のリストに復元する save_and_load を作ります。

つくる — 保存機能

この章では、ファイルの読み込み、書き込み、with、CSV 形式、try-except、例外の種類を扱いました。最後にそれらを 1 本につなぎます。

作るのは save_and_load(path, records) です。渡された辞書のリストを path に保存し、そのファイルを読み戻して、元と同じ形の辞書のリスト を返します。

records = [{"name": "りんご", "price": 120, "category": "食費"}] save_and_load("/tmp/kakeibo.txt", records) # [{"name": "りんご", "price": 120, "category": "食費"}]

入れたものと同じものが出てくるだけの、地味に見える関数です。しかしこれが成り立つということは、アプリを閉じても記録が失われない ということです。おこづかい帳が初めて「記憶」を持ちます。

保存と読み込みは必ず対で設計します。片方だけ先に作ると、書いた形式が読めないことに後から気づいて作り直しになります。

保存の流れ

書き出す側は前回までの組み合わせです。辞書を CSV の行にして、改行でつないで、with で書きます。

lines = [] for record in records: lines.append(record["name"] + "," + str(record["price"]) + "," + record["category"]) with open(path, "w", encoding="utf-8") as f: f.write("\n".join(lines))

join を使って ",".join([...]) と書いても同じです。読みやすいほうを選んでください。

復元の流れ

読み戻す側が今回の新しい部分です。行の文字列を、また辞書に戻します。

with open(path, "r", encoding="utf-8") as f: text = f.read() loaded = [] for line in text.splitlines(): name, price, category = line.split(",") loaded.append({"name": name, "price": int(price), "category": category})

split(",") で 3 つに分け、第 7 章のタプルアンパックで 3 つの変数に受けています。ここで int(price) を忘れない ことが最大の注意点です。

ファイルから読んだ値はすべて文字列です。price"120" という文字列のまま入っています。これを int に戻さないと、合計を計算するときに "120" + "80""12080" になる、という第 2 章で見た事故が起きます。

diagram (will load when visible)

書くときに str、読むときに int。この対称性が崩れると、型がじわじわ混ざって原因の分かりにくいバグになります。

空リストの扱い

記録が 0 件のときは、join の結果が空文字になり、中身が空のファイルができます。読み戻すと read"" を返し、splitlines[] を返します。for は 1 度も回らず、戻り値は [] です。

つまり特別な場合分けを書かなくても、空のときは空が返ります。splitlines を選んだ効果がここで効いています。split("\n") を使っていた場合、空文字を分割すると [""] という 1 要素のリストになり、line.split(",") が 1 つしか返せずアンパックで ValueError になります。

手元で動かしてみる

kakeibo.py に次のように書いて保存すると、実際に手元のフォルダにファイルができます。

def save_and_load(path, records): lines = [] for record in records: lines.append(record["name"] + "," + str(record["price"]) + "," + record["category"]) with open(path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) with open(path, "r", encoding="utf-8") as f: text = f.read() loaded = [] for line in text.splitlines(): name, price, category = line.split(",") loaded.append({"name": name, "price": int(price), "category": category}) return loaded print(save_and_load("kakeibo.txt", [{"name": "りんご", "price": 120, "category": "食費"}]))

実行したあと、同じフォルダに kakeibo.txt ができているはずです。テキストエディタで開くと りんご,120,食費 と書かれています。プログラムが残した最初の記録です。

よくある間違い

  1. int に戻し忘れる — 金額が "120" のまま返ると、見た目は似ていてもテストは落ちます。辞書に入れる前に int(price) を通します。
  2. キーの順番や名前を変える — 保存の形式は namepricecategory の順です。読み戻すときも同じ順で受け取ります。キー名は第 7 章から一貫して英語です。
  3. 書き込みのあと読まずに records を返す — テストは通ってしまうかもしれませんが、保存できたことの確認になりません。必ずファイル経由で戻します。

with を 1 つにまとめようとして詰まるのも定番です。書きモードのファイルからは読めないので、書きと読みで 2 回に分けます。

やってみよう

関数 save_and_load(path, records) を実装してください。records"品名,金額,カテゴリ" の行にして path に保存し、そのファイルを読み戻して辞書のリストに復元して返します。

金額は保存時に str、復元時に int を通します。records が空リストのときは空リスト [] が返る形になっていれば正解です。ファイルを開くときは with を使ってください。

要件

  1. 関数名は save_and_load で、引数は path と records の 2 つ
  2. 1 件を "品名,金額,カテゴリ" の行にし、改行でつないで path に書き出す
  3. ファイルを開くときは with を使う
  4. 書き出したファイルを読み戻し、split でカンマごとに分けて辞書に復元する
  5. 金額は int に戻し、文字列のままにしない
  6. records が空リストのときは空リストを返す

入出力例

save_and_load("/tmp/kakeibo_build_1.txt", [{"category":"食費","name":"りんご","price":120},{"category":"食費","name":"みかん","price":80}])[{"category":"食費","name":"りんご","price":120},{"category":"食費","name":"みかん","price":80}] save_and_load("/tmp/kakeibo_build_2.txt", [{"category":"日用品","name":"ノート","price":200}])[{"category":"日用品","name":"ノート","price":200}] save_and_load("/tmp/kakeibo_build_3.txt", [])[] save_and_load("/tmp/kakeibo_build_4.txt", [{"category":"その他","name":"もらいもの","price":0}])[{"category":"その他","name":"もらいもの","price":0}] save_and_load("/tmp/kakeibo_build_5.txt", [{"category":"交通費","name":"バス","price":220},{"category":"娯楽","name":"映画","price":1800},{"category":"日用品","name":"洗剤","price":380}])[{"category":"交通費","name":"バス","price":220},{"category":"娯楽","name":"映画","price":1800},{"category":"日用品","name":"洗剤","price":380}]

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.py
main.py
学習モード

メモ

つくる:保存機能

⌘S で保存