学生管理系统

浏览 1012

课文

## 需求概述 新的一个学期开始了,开学的第一天辅导员找到你,说班里学生太多,想让你帮着做一个学生管理系统。 你一想这不是很简单, 啪啪啪敲下了几行代码便交了上去。 ```python # 用数据与字典结合的形式保存数据 data = [] while True: inp = input('请输入新增、查看或退出:\n') if inp == '新增': # 接收用户数据,并去除前后空格 inp = input('输入姓名、性别、年龄,用空格作区分:\n').strip() name, gender, age = inp.split(' ') # 将数据拆分出来并以字典形式添加到数组中 data.append( dict( name=name, gender=gender, age=age, ) ) elif inp == '查看': print('当前共有%s名学生' % len(data)) # 循环打印数据 for item in data: print( '姓名:{name},性别:{gender},年龄:{age}'.format( name=item['name'], gender=item['gender'], age=item['age'] ) ) elif inp == '退出': break else: print('输入错误请重新输入') print('\n') ``` 你运用了之前所学到的知识,使用了列表与字典类型保存数据。 使用了 while 循环接收用户的操作, 用 input 接收输入并对其字符串拆分。 ## pickle 序列化 美美地等着辅导员夸奖的你,等来了辅导员的电话,他告诉你他花半小时录入的数据在重启程序后消失, 你一拍腿大呼忘记将其持久化到硬盘,于是便运用了上个章节学到的文件操作进行修改。 因为文件的操作写入时接收二进制或字符串类型,便需要将我们数组与字典组合的类型`序列化`成字符串加以保存。 什么是序列化? 序列化便是以一定`规范`将对象转成二进制或字符,等到需要时再将其解析回对象。 常在需要保存对象到文件或数据库,再或者网络传输时使用。 在这里我们使用内置的`pickle`模块来序列化和反序列化。 ```python import os import pickle # 用大写变量名来表示一些常量 DATA_PATH = './data' # 用数据与字典结合的形式保存数据 data = [] # 判断文件是否存在 if os.path.exists(DATA_PATH): with open(DATA_PATH, 'r') as f: data_str = f.read() data = pickle.loads(data_str) while True: inp = input('请输入新增、查看或退出:\n') if inp == '新增': # 接收用户数据,并去除前后空格 inp = input('输入姓名、性别、年龄,用空格作区分:\n').strip() name, gender, age = inp.split(' ') # 将数据拆分出来并以字典形式添加到数组中 data.append( dict( name=name, gender=gender, age=age, ) ) elif inp == '查看': print('当前共有%s名学生' % len(data)) # 循环打印数据 for item in data: print( '姓名:{name},性别:{gender},年龄:{age}'.format( name=item['name'], gender=item['gender'], age=item['age'] ) ) elif inp == '退出': break else: print('输入错误请重新输入') print('\n') with open(DATA_PATH, 'w') as f: f.write(pickle.dumps(data)) ``` 我们运用之前学到的知识,在一开始读取文件并用 loads 将二进制解析成数组。 并在之后退出程序时将 data 数组 dumps 成二进制。 在你刚写完的时候,辅导员来了电话,说他还需要更新数据功能。 你一寻思,辅导员是典型的甲方心态。一开始自己心里没个数,一会说加个功能,一会说修改个功能。 代码经常修改的话, 会影响到开发封闭原则,各模块相互引用如盘丝洞般错综复杂,不小心就变成了屎山。 所以你想到了可以面向对象的方式重构代码,面向对象的三大特征是抽象、继承、多态,可以让你很容易地应对之后的修改与功能的新增。 `面向对象设计是对付不合理甲方的神器。` 首先我们确定三个类。 ```python class Student: # 学生对象 def __init__(self, name, gender, age): pass def __str__(self): # 打印时的显示 pass class Op: # 所有操作类的父类 @staticmethod def hander(data): raise NotImplementedError() class Manager: # 负责程序的管理,数据的读取与保存 def register(self, op): # 注册操作 pass def unregister(self, op): # 取消注册 pass def load(self): # 从文件读取数据 pass def save(self): # 保存数据到文件 pass def loop(self): # 执行循环 pass ``` 以上就是我们大致定义到的接口,针对其分别进行实现。 ```python import os import pickle DATA_PATH = './data' class Student: def __init__(self, name, gender, age): self.name = name self.gender = gender self.age = age def __str__(self): return '姓名:{name},性别:{gender},年龄:{age}'.format( name=self.name, gender=self.gender, age=self.age ) class Op: action = '' @staticmethod def handle(data): raise NotImplementedError() class Manager: ops = {} data = [] def __init__(self): self.load() def load(self): # 从文件读取数据 # 判断文件是否存在 if os.path.exists(DATA_PATH): with open(DATA_PATH, 'rb') as f: self.data = pickle.load(f) def save(self): # 保存数据到文件 with open(DATA_PATH, 'wb') as f: pickle.dump(self.data, f) def register(self, op): # 注册操作 self.ops[op.action] = op def unregister(self, op): # 取消注册 if op.action in self.ops: self.ops[op.action] def loop(self): # 执行循环 while True: inp = input( '请输入%s或退出:\n' % ','.join([action for action in self.ops.keys()]) ) if inp == '退出': break if inp in self.ops: self.ops[inp].handle(self.data) else: print('输出错误') self.save() ``` 我们完成了以上的东西, 这时我们定义一个 OpAdd 类 与 OpList 继承自 Op 类。 并将其注册到 manager 中,运行我们的程序。 ```python class OpAdd(Op): action = '增加' @staticmethod def handle(data): inp = input('输入姓名、性别、年龄,用空格作区分:\n') name, gender, age = inp.split(' ') data.append(Student(name, gender, age)) print('增加成功') return data class OpList(Op): action = '查看' @staticmethod def handle(data): print('当前共有%s名学生' % len(data)) # 循环打印数据 for student in data: print(student) manager = Manager() manager.register(OpAdd) manager.register(OpList) # 执行 manager.loop() ``` 到此完成了我们的程序,测试一下! ```output 请输入增加,查看或退出: 增加 输入姓名、性别、年龄,用空格作区分: 小明 男 20 增加成功 请输入增加,查看或退出: 增加 输入姓名、性别、年龄,用空格作区分: 鸣人 男 18 增加成功 请输入增加,查看或退出: 查看 当前共有2名学生 姓名:小明,性别:男,年龄:20 姓名:鸣人,性别:男,年龄:18 请输入增加,查看或退出: 退出 ```

实战

1.甲方的需求是无止尽的,完善上面的程序! - 增加删除功能 - 增加按名字搜索功能

解析

1.甲方的需求是无止尽的,完善上面的程序! - 增加删除功能 - 增加按名字搜索功能 ```python class OpDel(Op): action = '删除' @staticmethod def handle(data): inp = input('输入要删除的姓名:\n') for student in data[:]: if student.name == inp: data.remove(student) print('删除了:%s' % student) class OpSearch(Op): action = '搜索' @staticmethod def handle(data): inp = input('输入要搜索的姓名:\n') for student in data: if inp in student.name: print('搜索到:%s' % student) ```
点击查看

评论

登录参与讨论

刘德华山东分华

该内容已删除

04-16

回复

style

测试回复

@刘德华山东分华

05-19

回复

非洲小白脸

08-20

回复

非洲小白脸

是w改成wb 手误

@非洲小白脸

08-20

回复

非洲小白脸

r改成rb

@非洲小白脸

08-20

回复

非洲小白脸

讲错了是w改成wb  不是rb哈 手误

08-20

回复

非洲小白脸

r改成rb

08-20

回复

非洲小白脸

解析1中删除代码存在问题,出现同名现象时只会删除第一个出现的 原因是,删除list中的元素后,list的实际长度变小了,但是循环次数没有减少,依然按照原来list的长度进行遍历,所以会造成索引溢出。 解决办法1.拷贝原列表 data[:]是对原列表的一个拷贝,然后对原列表进行删除操作,这种做法缺点是太占空间 2.从后往前遍历列表,删除 3.使用set去重 并使用sort保持原排序 这种做法感觉不适用于实际,当然实际也不应该用名字删除而是用学号删除,这里不贴出代码了

08-20

回复

西瓜lalavilaxxww

观察很细致,列表删除确实存在问题,我完善一下代码。

@非洲小白脸

09-04

回复

共 5 条
  • 1
前往
  • 1