@runzhliu
2018-02-09T03:53:35.000000Z
字数 2473
阅读 1099
Python 字典 元组
参考资料
《Effective Python》
通常人们习惯用 Python 的内置字典类型来保存对象在生命周期里的动态内部状态。举个简单的例子:
class SimpleGradebook(object):def __init__(self):self._grades = {}def add_student(self, name):self._grades[name] = []def report_grades(self, name, score):self._grades[name].append(score)def average_grade(self, name):grades = self._grades[name]return sum(grades) / len(grades)
从示例程序来看,SimpleGradebook 这个类代表简易的成绩册的意义,通过用内部的 _grades 字典来保存学生的姓名和成绩,并且提供方法 report_grades 了来登记学生的姓名和成绩,此外还提供一个计算所有登记在册的学生的平均成绩的方法 average_grade。
字典用起来是很方便的,并且效率也很高,计算复杂度很低。然后当需求增多的时候,其实并不好扩展,比如现在需要成绩册可以记录不同科目的成绩,并且可以根据不同科目来求平均成绩。
class BySubjectGradebook(object):def __init__(self):self._grades = {}def add_student(self, name):self._grades[name] = {}def report_grade(self, name, subject, grade):by_subject = self._grades[name]grade_list = by_subject.setdefault(subject, [])grade_list.append(grade)def average_grade(self, name):by_subject = self._grades[name]total, count = 0, 0for grades in by_subject.values():total += sum(grades)count += len(grades)return total / count
上例中的类为 BySubjectGradebook,代表记录不同科目的成绩,可以看到,在录入成绩的方法 report_grade 增加科目参数,并且增加 grade_list 来记录不同科目的成绩列表,同时也可以看到 average_grade 计算成绩的平均分的方法代码膨胀,而已有嵌套循环,这让人头大......
然而这还不是最糟糕的,当年级长需要班主任提供一份该班的「平均成绩」,并且由于「素质教育」,各个科目都有相应权重,比如说语数英权重更高,音乐体育权重较低,所以成绩册还得加一个功能,就是计算加权平均分的功能。
import collectionsGrade = collections.namedtuple('Grade', ('score', 'weight'))class Subject(object):def __init__(self):self._grades = []def report_grade(self, score, weight):self._grades.append(Grade(score, weight))def average_grade(self):total, total_weight = 0, 0for grade in self._grades:total += grade.score * grade.weighttotal_weight += grade.weightreturn total / total_weightclass Student(object):def __init__(self):self._subjects = {}def subject(self, name):if name not in self._subjects:self._subjects[name] = Subject()return self._subjects[name]def average_grade(self):total, count = 0, 0for subject in self._subjects.values():total += subject.average_grade()count += 1return total / countclass Gradebook(object):def __init__(self):self._students = {}def student(self, name):if name not in self._students:self._students[name] = Student()return self._students[name]if __name__ == '__main__':book = Gradebook()oscar = book.student('oscar')math = oscar.subject('Math')math.report_grade(80, 0.1)print oscar.average_grade()
可以看到新的具有计算加权平均分功能的类 Grade 代码量其实并不小,但是仔细去阅读的话,可以发现代码逻辑会更加清晰。利用 collections 模块中的 namedtuple 类型可以定义出精简而有不可变的数据数据类型。
上述代码中构建了一个 Grade 结构,会记录某科目的平均分和该科目的权重 weight,并且把科目、学生各构建一个辅助类来记录数据,这样使得最后的成绩册类 Gradebook 的表达更加清晰。在使用上,只需要新建一个 Student 的对象,然后再新建一个该学生的 subject 对象,再去登记分数和权重,最后调用计算加权平均分的方法即可。
