何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

『Effective Python』Item 22: 辞書やタプルで保持するよりもヘルパークラスを使おう

『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の主題。 「こうなったら」というのは、大体次のようになる。

  1. 辞書の多重化を避ける。つまり辞書の値に辞書を使うのは避けよう。
  2. データ構造としてのタプルは、3つ組以上ならばcollectionsモジュールのnamedtupleを使いましょう。
  3. namedtupleはタプルなので以下の懸念事項があるので手に負えなくなったら自作クラスを書きましょう。
    • デフォルト値が無い
    • 属性によるアクセスだけでなくインデックス、for文でイテレート可能なので想定外の操作ができてしまう