『Effective Python』の続き。リファクタリング! www.effectivepython.com
Pythonの辞書は便利だなあと思いつつ、次から次へと湧いてくる仕様変更・追加機能に対応していくと酷い目に合う。 生徒の成績を管理するコンテナを作るという題材で、継ぎ足し継ぎ足しで作られたのが次のコード。
class WeightedGradebook(object): def __init__(self): self._grades = {} def add_student(self, name): self._grades[name] = {} def report_grade(self, name, subject, score, weight): by_subject = self._grades[name] grade_list = by_subject.setdefault(subject, []) grade_list.append((score, weight)) def average_grade(self, name): by_subject = self._grades[name] score_sum, score_count = 0, 0 for subject, scores in by_subject.items(): subject_avg, total_weight = 0, 0 for score, weight in scores: subject_avg += score * weight total_weight += weight score_sum += subject_avg / total_weight score_count += 1 return score_sum / score_count
こうなったら、ヘルパークラスを使って機能ごとにクラスを書いてリファクタリングしようね、というのがItem 22の主題。 「こうなったら」というのは、大体次のようになる。
- 辞書の多重化を避ける。つまり辞書の値に辞書を使うのは避けよう。
- データ構造としてのタプルは、3つ組以上ならば
collections
モジュールのnamedtuple
を使いましょう。 namedtuple
はタプルなので以下の懸念事項があるので手に負えなくなったら自作クラスを書きましょう。- デフォルト値が無い
- 属性によるアクセスだけでなくインデックス、
for
文でイテレート可能なので想定外の操作ができてしまう