CSV形式で保存

このレッスンで作るもの — 辞書のリストを「品名,金額,カテゴリ」の行に変換する to_csv_lines を作り、保存に適した形を手に入れます。

CSV 形式で保存する

ファイルに書けるのは文字列だけです。ところがおこづかい帳の記録は、第 7 章で決めたとおり辞書のリストです。

[{"name": "りんご", "price": 120, "category": "食費"}]

これをそのままファイルに書くことはできません。辞書を 1 行の文字列にする決まりごと を自分で決める必要があります。

もっとも広く使われている決まりが CSV です。Comma Separated Values の頭文字で、値をカンマで区切って並べるだけの素朴な形式です。

りんご,120,食費 みかん,80,食費

1 行が 1 件、カンマの区切りで 3 つの項目が並びます。表計算ソフトでもそのまま開けるので、保存先として扱いやすい形です。

保存の形式は、あとから読み戻せることがすべてです。凝った形にするより、区切りが一目で分かる形を選ぶほうが長持ちします。

join で行を組み立てる

1 件の辞書を 1 行にするには join を使います。前回までは行と行を "\n" でつなぎましたが、今回は項目と項目を "," でつなぎます。

record = {"name": "りんご", "price": 120, "category": "食費"} line = ",".join([record["name"], str(record["price"]), record["category"]])

結果は "りんご,120,食費" です。ここで大事なのが str(record["price"]) の部分です。priceint なので、join にそのまま渡すと TypeError になります。join がつなげるのは文字列だけです。

全件を変換するには for で回してリストに append します。

def to_csv_lines(records): lines = [] for record in records: lines.append(",".join([record["name"], str(record["price"]), record["category"]])) return lines

第 7 章で学んだリスト内包表記でも書けます。どちらでも構いません。

split で読み戻す

join の逆が split です。"," で分割すると、3 つの文字列のリストに戻ります。

name, price, category = "りんご,120,食費".split(",")

第 7 章のタプルアンパックがそのまま使えます。ただし price はこの時点では文字列の "120" です。数値として計算に使うには int(price) が要ります。

diagram (will load when visible)

書き出すときに str で文字列にした値は、読み戻すときに int で数値に戻します。行きと帰りが対になっている、と覚えると迷いません。

動きを追ってみる

to_csv_lines に 2 件の記録を渡したときの流れを追います。

  1. 空のリスト lines を用意する
  2. 1 件目の {"name": "りんご", "price": 120, "category": "食費"} を取り出す
  3. 3 つの値を "," でつないで "りんご,120,食費" を作り、append する
  4. 2 件目も同じように処理して "みかん,80,食費" を追加する
  5. ["りんご,120,食費", "みかん,80,食費"] が戻り値になる

records が空リストのときは for が 1 度も回らず、空リストがそのまま返ります。「まだ 1 件も記録がない」場合に自然に対応できます。

よくある間違い

  1. str を付け忘れる",".join([name, price, category]) のように int を混ぜると TypeError になります。エラーメッセージは sequence item 1 と、何番目が悪いかを教えてくれます。
  2. 区切り文字を混同する — 項目の区切りは ","、行の区切りは "\n" です。両方 join で書くので取り違えやすい部分です。
  3. 品名にカンマが入る"りんご, みかんセット" のようにカンマを含む品名を保存すると、読み戻したときに項目数が合わなくなります。このコースでは品名にカンマを使わない前提にしますが、実務では標準ライブラリの csv を使ってこの問題を避けます。

金額を str にする位置を間違えて str(record) としてしまうのも定番です。辞書全体が文字列になり、まったく違う行ができます。

やってみよう

関数 to_csv_lines(records) を実装してください。records{"name": ..., "price": ..., "category": ...} の形の辞書のリストです。1 件を "品名,金額,カテゴリ" の文字列にして、その並びをリストで返します。

金額は str で文字列にしてから join します。区切りはカンマだけで、前後に空白は入れません。records が空リストのときは空リスト [] が返る形になっていれば正解です。

要件

  1. 関数名は to_csv_lines で、引数は records の 1 つ
  2. 1 件を "品名,金額,カテゴリ" の順にカンマでつなぐ
  3. 金額は str で文字列にしてから join する
  4. カンマの前後に空白を入れない
  5. records が空リストのときは空リストを返す

入出力例

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

ヒント

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

メモ

CSV形式で保存

⌘S で保存