第22条: 尽量用辅助类来维护程序的状态,而不要用字典
带着问题学习
- 如何使用字段维护程序的状态?
- 如何使用辅助类为辅程序的状态?
- 有没有更好的方式?
总结
- 1.1 使用字典维护程序的状态(简单示例)
需求:
记录学生的名字,记录学生对应的科目(假设一个科目)的多次成绩
代码实现:
class SimpleGradebook(object):
def __init__(self):
self.grades = {}
def add_student(self, name):
if name in self.grades:
raise ValueError('no named {}'.format(name))
self.grades[name] = []
def report_grade(self, name, score):
if name not in self.grades:
raise ValueError('no named {}'.format(name))
self.grades[name].append(score)
def average_grade(self, name):
grades = self.grades[name]
return sum(grades) / len(grades)
book = SimpleGradebook()
book.add_student('xiaoming')
book.report_grade('xiaoming', 90)
book.report_grade('xiaoming', 80)
print(book.average_grade('xiaoming'))
# 85
book.add_student('liming')
book.report_grade('liming', 60)
book.report_grade('liming', 65)
print(book.average_grade('liming'))
#62
print(book.grades)
# {'liming': [60, 65], 'xiaoming': [90, 80]}
- 1.2 使用字典维护程序的状态(中等复杂度示例)
# 记录每个学生对应的各科的多次成绩,最后求所有成绩的平均成绩
class BySubjectGradebook(object):
def __init__(self):
self.grades = {}
def add_student(self, name):
if name in self.grades:
raise ValueError('no named {}'.format(name))
self.grades[name] = {}
def report_grade(self, name, subject, grade):
if name not in self.grades:
raise ValueError('no named {}'.format(name))
by_subject = self.grades[name]
grade_list = by_subject.setdefault(subject, [])
grade_list.append(grade)
def average_grade_of_all_subjects(self, name):
subjects_grades = self.grades[name]
total, count = 0, 0
for grades in subjects_grades.values():
total += sum(grades)
count += len(grades)
return total / count
book = BySubjectGradebook()
book.add_student('xiaoming')
book.report_grade('xiaoming','Math', 90)
book.report_grade('xiaoming', 'Math',80)
book.report_grade('xiaoming', 'Gym',70)
book.report_grade('xiaoming', 'Gym',80)
print(book.average_grade_of_all_subjects('xiaoming'))
# 80
- 1.3 使用字典维护程序的状态(比较复杂,示例)
# 记录每个学生对应的各科成绩不同考试对应的成绩与权重比例
class WeightGradebook(object):
def __init__(self):
self.grades = {}
def add_student(self, name):
if name in self.grades:
raise ValueError('no named {}'.format(name))
self.grades[name] = {}
def report_grade(self, name, subject, score, weight):
if name not in self.grades:
raise ValueError('no named {}'.format(name))
by_subject = self.grades[name]
grade_list = by_subject.setdefault(subject, [])
grade_list.append((score, weight))
def average_grade_of_all_subjects(self, name):
subjects_grades = self.grades[name]
score_sum, subject_count = 0, 0
for subject, scores in subjects_grades.items():
subject_avg, subject_total_score, total_weight = 0, 0, 0
for score, weight in scores:
subject_total_score += score*weight
total_weight += weight
subject_avg = subject_total_score / total_weight
score_sum += subject_avg
subject_count += 1
print(subject_count)
return score_sum / subject_count
book = WeightGradebook()
book.add_student('xiaoming')
book.report_grade('xiaoming', 'Math', 90, 0.6)
book.report_grade('xiaoming', 'Math', 80, 0.4)
book.report_grade('xiaoming', 'Gym', 70, 0.7)
book.report_grade('xiaoming', 'Gym', 80, 0.3)
print(book.grades)
print(book.average_grade_of_all_subjects('xiaoming'))
# {'xiaoming': {'Gym': [(70, 0.7), (80, 0.3)], 'Math': [(90, 0.6), (80, 0.4)]}}
# 2
# 79.5
2.1 使用辅助类来维护程序的状态
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
print('total:{},total_weight:{}'.format(total, total_weight))
average_grade = total / total_weight
print('average_grade:{}'.format(average_grade))
return average_grade
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
print('total:{},count:{}'.format(total, count))
return total / count
class Gradebook(object):
def __init__(self):
self._students = {}
def students(self, name):
if name not in self._students:
self._students[name] = Student()
return self._students[name]
book = Gradebook()
libai = book.students('libai')
liming = book.students('liming')
math = libai.subject('math')
math.report_grade(80, 0.1)
math.report_grade(90, 0.2)
print(libai.average_grade())
# total:26.0,total_weight:0.3
# average_grade:86.6666666667
# total:86.6666666667,count:1
# 86.6666666667
-
2.2 将数据持久化
将类对的属性与表中的字段对应,不同类型之间的关系可以使用外键关联,按需求实现多对多还是一对多等对应的关系,flask 框架可以使用 继承Model类,然后编写对应的字段 -
3 总结
- 不要使用嵌套字典,不要使用长元组
- 简单不可变数据可以用namedtuple
- 字典复杂需要拆分为辅助类,比如uwsgi中的spool函数只接受一层级的字典,尽量简单,便宜维护