1.5 应用:学生记录
提取学生信息,产生相应的报告。
对于每一个学生,有五部分信息
- ID,以整数表示;
- 姓名,以字符串表示;
- 年级,以整数代号表示;
- GPA,以浮点数表示。
但是我们并不知道,这些学生的信息是怎么储存的,它们可能储存在纯文本文件中,在二进制文件中,甚至是在数据库中。
1.5.1 设计解决方法
无论学生信息是怎么储存的,从外部提取信息都分为以下3个步骤:
- 建立连接;
- 提取每一条记录;
- 关闭连接。
我们定义学生文件读取器ADT,来表示对学生信息的提取。注意读取器(Reader)是是一个用于从外部读取输入数据的对象,而写入器(writer)是一个用于向外部输出数据的对象。
学生文件读取器ADT包含如下方法:
- StudentFileReader(filename):创建实例,用于从给定的文件名的文件中读取数据;
- open():与输入源建立连接,准备提取学生记录;
- close():关闭与输入源的连接,若连接已关闭,则引发异常;
- fetchRecord():从输入源中提取下一行学生记录,并返回包含这一数据的储存对象的引用,若连接已关闭,则引发异常;
- fetchAll():从输入源提取所有学生信息,以列表形式返回。
创建报告
如前面所述,我们可以先不管学生文件读取器ADT如何实现,先使用这一ADT来生成相应报告。
#-*-coding: utf-8-*-
# 从外部来源提取数据,生成学生报告
from studentfile import StudentFileReader
# 要打开的文件名
FILE_NAME = "students.txt"
# 打印学生报告
def printReport(theList):
#
classNames = (None, "Freshman", "Sophomore", "Junior", "Senior")
# 打印头部
print "LIST OF STUDENTS".center(50)
print ""
print "%-5s %-25s %-10s %-4s" % ('ID', 'NAME', 'CLASS', 'GPA')
print "%5s %25s %10s %4s" % ('-' * 5, '-' * 25, '-' * 10, '-' * 4)
# 打印主体部分
for record in theList:
print "%5d %-25s %-10s %4.2f" % (record.idNum, record.lastName + ', ' + record.firstName, classNames[record.classCode], record.gpa)
# 增加页脚
print "-" * 50
print "Number of students:", len(theList)
def main():
# 从给定的文件中提取出学生记录
reader = StudentFileReader(FILE_NAME)
reader.open()
studentList = reader.fetchAll()
reader.close()
# 按照ID排列,使用lambda函数(返回学生的ID)。
studentList.sort(key = lambda rec: rec.idNum)
# 打印学生报告
printReport(studentList)
if __name__ == "__main__":
main()
当每一条学生记录从输入源中读入后,会被暂时储存在一个储存对象中。这个储存对象可以是列表或元组。在这里,我们使用类来实现这一目的,这被称为储存类(Storage class)。储存类中,只有构造器方法,即__init__,而没有其他方法。
储存类应在同一模块定义(在这里是studentfile)。储存类有时可能被设置为模块私有,无法从外部调用,只供模块内使用。这种储存类以单下划线开头。
在这里,在前述代码中,也是用了储存类(猜测可能是由于studentList中存储的student就是储存类的实例的缘故)。(说实话,这里不是很明白,看来还得在复习类的私有属性)
1.5.2 ADT的实现
假设输入源是纯文本文件,即txt格式,每一行是一个学生的一条记录,如ID、姓、名、年级或者GPA等。
#-*-coding: utf-8-*-
# 实现“学生文件读取器”这一抽象数据类型
class StudentFileReader(object):
# 创建新的学生读取器实例
def __init__(self, inputSrc):
self._inputSrc = inputSrc # 文件名
self._inputFile = None
# 创建输入文件的连接
def open(self):
self._inputFile = open(self._inputSrc, 'r') # 本质是文件对象
# 关闭输入文件的连接
def close(self):
self._inputFile.close()
self._inputFile = None
# 提取所有学生记录,并储存到一个列表中
def fetchAll(self):
theRecords = list()
student = self.fetchRecord()
while student != None:
theRecords.append(student)
student = self.fetchRecord()
return theRecords
# 从输入文件中提取下一个学生记录
def fetchRecord(self):
# 读取第一行记录
line = self._inputFile.readline()
if line == '':
return None
# 如果有记录,则创建储存对象
student = StudentRecord()
student.idNum = int(line)
student.firstName = self._inputFile.readline().rstrip()
student.lastName = self._inputFile.readline().rstrip()
student.classCode = int(self._inputFile.readline())
student.gpa = float(self._inputFile.readline())
return student
# 用于储存学生记录的储存类
class StudentRecord(object):
def __init__(self):
self.idNum = 0
self.firstName = None
self.lastName = None
self.classCode = 0
self.gpa = 0.0