つくる 集計のリファクタ
このレッスンでできるようになること。第2章で for 文で書いた集計を、補助関数を使いながら内包表記とジェネレータ式に書き直し、同じ結果になることを確かめられるようになります。
新しい文法はここでは出てきません。自分で書くのは、この章で覚えた辞書内包表記、set 内包表記、ジェネレータ式を使う3行です。動いているコードを、結果を変えずに読みやすい形へ直す作業をリファクタリングと呼びます。中級で身に付けたい大事な技術のひとつです。
書き直すのは、家計簿の月次レポートが使っている次の3つの集計です。
- 月ごとの合計金額を出す処理。月をキー、合計を値とする辞書を作ります
- これまでに使ったカテゴリの一覧を出す処理。重複を落として並べます
- 全期間の総額を出す処理。月ごとの合計を足し上げます
第2章では、それぞれ空の入れ物を用意してから for でまわして詰めていました。1は辞書内包表記、2は set 内包表記と sorted、3はジェネレータ式と sum が、そのまま当てはまります。
一度に入れ子の処理まで書き直す必要はありません。演習では、支出リストの金額を合計する _sum_amounts(items) と、全月の支出を1つのリストに集める _all_expenses(monthly) を用意します。補助関数の中の for 文はそのまま使い、summarize の3つの集計だけを埋めてください。
補助関数を使うと、内包表記の値を作る部分を短くできます。別の題材として、棚ごとの長い題名の冊数を数える例を見ます。
Python
def count_long_titles(titles):
return len([title for title in titles if len(title) >= 3])
shelves = {"東": ["森", "夜の図鑑"], "西": ["海の物語"]}
counts = {shelf: count_long_titles(titles) for shelf, titles in shelves.items()}
print(counts) # {'東': 1, '西': 1}辞書内包表記はキーと値の組を作る役割に集中し、冊数の計算は補助関数に任せています。演習でも月ごとの合計にこの分担を使います。
リファクタリングで気をつけるのは、結果を変えないことです。手順としては、書き直す前の結果をひかえておき、書き直したあとに同じデータを流して見比べる、という進め方が安全です。今回はテストケースがその役割を果たします。
3つの結果は、別々に返すよりも1つの辞書にまとめたほうが、レポート側から使いやすくなります。演習では返却用の辞書を準備済みにしてあります。カテゴリ一覧は sorted で文字コード順に並べ、全期間の総額は月ごとの合計をジェネレータ式で sum に渡して求めます。
書き終えたら、行数がどれだけ減ったかを数えてみてください。短くなること自体が目的ではありませんが、処理の意図が1行で読み取れるようになったかどうかは、良いリファクタリングかを判断する目安になります。
要件
- monthly_total は辞書内包表記で作り、monthly.items() から受け取る月をキー、_sum_amounts(items) の結果を値にする
- categories は準備済みの expenses から set 内包表記でカテゴリ名を集め、sorted で文字コード順のリストにする
- grand_total は monthly_total.values() の値をジェネレータ式で sum に渡して求める
- 補助関数と返却用の辞書は変更せず、summarize の集計には for 文と append を使わない