第22条:尽量用辅助类来维护程序状态,而不是字典和元组
2018-05-29 本文已影响0人
小懒额
现在如果用一段代码来实现通过学生成绩求出学生平均成绩,一般会这样做:
class SimpleGradebook(object):
def __init__(self):
self._grades = {}
def add_student(self, name):
self._grades[name] = []
def report_grade(self, name, score):
self._grades[name].append(score)
def average_grade(self, name):
grades = self._grades[name]
return sum(grades)/len(grades)
这里,使用字典来保存学生的姓名和对应的多次成绩,可以看出,在 Python 中,字典很好的处理了数据的动态变化。这时,如果需要把成绩细化成不同科目的成绩,就需要去保存科目信息了。
class SubjectGradebook(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, 0
for grades in by_subject.values():
total += sum(grades)
count += len(grades)
return total/count
这里的结构有所变化,原先用来保存多次成绩的列表被替换成了字典,和保存成绩的字典形成了两层嵌套。也就是成绩的字典里面保存了学生姓名和对应的科目成绩字典,科目成绩字典保存了不同科目和对应科目的多次成绩。这样改,没有太麻烦,看上去还过得去。
现在,如果给每科添加一个权重,不同考试所占权重不同,比如平时考试权重较低,期中期末考试权重较高,在计算平均分时又会有变化。
class WeightGradeBook(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]
total, total_weight = 0, 0
for grades in by_subject.values():
total += grades[0]*grades[1]
total_weight += (grades[1])
return total / total_weight
因为每次成绩都有一个权重,这样在保存成绩的时候还要把权重一起加进去,可以使用元组来实现这个需求。也就是原先保存多次成绩的列表中的元素变成了一个个元组。这样可以实现,但是代码看上去就不是很清晰,而且多种结构冗在一起,不利于维护。
所以,我们可以使用辅助类来做这些数据,把不同数据用一个个类来分开处理。
import collections
Grade = 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, 0
for grade in self._grades:
total += grade.score * grade.weight
total_weight += grade.weight
return total / total_weight
class 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, 0
for subject in self._subjects.values():
total += subject.average_grade()
count += 1
return total/count
class 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]
这里用到了一个 nametuple,一般来说,元组中的元素超过两个的时候,元组中的元素再进行扩张的时候,就很难理解每个元素的作用,这样用 nametuple 来保存,便于理解。
可以看到,对于不同的数据,都使用了辅助类来实现,解除了之前代码中的多重嵌套,让代码看上去很清晰,用起来也是很方便。
book = GradeBook()
user = book.student('xiaoming')
math = user.subject('math')
math.report_grade(90, 0.1)
math.report_grade(100, 0.1)
print(user.average_grade())
>>>
95.0