# coding:utf-8
# @Author:dx
# @Time:2023-4-20
# @File:学生信息管理系统(面向对象).py
"""
学生信息管理系统
"""
import pickle
print('欢迎使用学生信息管理系统~>_<~')
# 创建一个自定义的异常类,用于得知输入的服务序号是否超出了范围
class OrderNumberError(Exception):
def __init__(self, message):
self.message = message
class NotArgError(Exception):
def __init__(self, message):
self.message = message
class StudentsInfo(object):
"""类中的函数没有先后顺序,无论定义在哪里都可以获取到"""
# 将字典对象传入类中
def __init__(self, student):
self.students = student
# 按学号查找学生信息
def get_by_id(self, students_id):
stu_data = self.students.get(students_id)
return '学号{},姓名{},年龄{},班级{},性别{}'.format(students_id, stu_data['name'], stu_data['age'], stu_data['class_number'], stu_data['sex'])
# 获取所有的学生信息5
def get_all(self):
for _id, value in self.students.items():
print('学号{},姓名{},年龄{},班级{},性别{}'.format(_id, value['name'], value['age'], value['class_number'], value['sex']))
return self.students
# 检查学生信息是否正确
@staticmethod
def check_user_info(**kwargs):
assert len(kwargs) == 4, '参数必须是四个'
if 'name' not in kwargs:
raise NotArgError('缺少学生姓名参数')
if 'age' not in kwargs:
raise NotArgError('缺少学生年龄参数')
if 'class_number' not in kwargs:
raise NotArgError('缺少学生班级参数')
if 'sex' not in kwargs:
raise NotArgError('缺少学生性别参数')
name_value = kwargs['name']
age_value = kwargs['age']
class_number_value = kwargs['class_number']
sex_value = kwargs['sex']
if not isinstance(name_value, str):
raise TypeError('name应该是字符串类型')
if not isinstance(age_value, int):
raise TypeError('age应该是整数类型')
if not isinstance(class_number_value, int):
raise TypeError('class_number应该是整数类型')
if not isinstance(sex_value, str):
raise TypeError('sex应该是字符串类型')
# 添加学生(单个)
def add(self, **student):
try:
self.check_user_info(**student)
except Exception as e:
raise e
self.__add(**student)
# 添加成员(批量)
def adds(self, new_students):
for student in new_students:
try:
self.check_user_info(**student)
except Exception as e:
print(e, student.get('name'))
continue
self.__add(**student)
# 用于将学号和新添加的学生成员绑定传入字典中
def __add(self, **student):
try:
new_id = max(self.students) + 1
self.students[new_id] = student
except ValueError:
new_id = 1
self.students[new_id] = student
# 用于删除单个成员
def delete(self, students_id):
if students_id not in self.students:
print('{}不存在'.format(students_id))
else:
deleted_students = self.students.pop(students_id)
print('学号为{}, 姓名为{}的学生已删除'.format(students_id, deleted_students['name']))
# 批量删除成员
def deletes(self, ids):
for student in ids:
self.delete(student)
# 修改单个成员
def update(self, students_id, **kwargs):
if students_id not in self.students:
print('不存在这个学号')
return
try:
self.check_user_info(**kwargs)
except (NotArgError, TypeError) as e:
raise e
self.students[students_id] = kwargs
print('学生信息更新完毕!')
# 批量修改成员
def updates(self, update_students): # TODO: 需要改进
for student in update_students:
try:
_id = list(student.keys())[0]
except IndexError as e:
print(e)
continue
user_info = student[_id]
if _id not in self.students:
print(f'{_id}学号不存在')
continue
try:
self.check_user_info(**user_info)
except Exception as e:
print(e)
continue
"""
if check != True` 和 `if not check` 在大多数情况下是等价的,都是检查`check`是否为False。
但是这两个表达式在处理某些特定数据类型时可能会出现不同的行为。
如果`check`是一个自定义类的实例,那么 `check != True` 可能会调用`__ne__`方法(与`__eq__`相对),而 `not check`
则会调用`__bool__`或`__len__`方法来确定对象的“真值”(truth value)。因此,这两个表达式在这种情况下可能会产生不同的结果。
另外,如果 `check` 是一个可调用对象,例如一个函数或一个lambda表达式,那么 `not check` 会尝试调用它并检查结果是否为False,
而 `check != True` 则会检查 `check` 是否返回`True`或`False`。
总之,对于大多数数据类型,这两个表达式是等价的,但在某些特定情况下,它们可能会产生不同的结果。
"""
self.students[_id] = user_info
print('学生信息更新完毕!')
# 用于模糊匹配成员
def search(self, **kwargs):
"""
self.students = {1: {'name': '小舒', 'age': 18, 'class_number': 6, 'sex': 'famale'},
2: {'name': 'xiaoshu', 'age': 18, 'class_number': 2, 'sex': 'famale'},
3: {'name': 'xiaoding', 'age': 18, 'class_number': 5, 'sex': 'famale'},
4: {'name': 'xiaozhao', 'age': 19, 'class_number': 4, 'sex': 'male'}}
"""
datas = self.students # 创建一个指向学生信息的变量
result = []
compare = []
if sum(i is not None for i in list(kwargs.values())) != 0: # 判断user输入的信息是否全为空
pass
else:
raise NotArgError('没有发现搜索的关键词!')
for key in datas:
value = (self.students[key])
number = 0 # 用于存储查找到的用户的优先值,符合的信息项越多对应的优先值也就越高,后面会根据这个优先值,来把查找到的学生信息进行排序
if kwargs['name']: # 判断用户是否输入了姓名
if kwargs['name'] in str(value['name']):
number += 1 # 优先值+1
else:
continue
if kwargs['age']: # 判断用户是否输入了年龄
if str(kwargs['age']) in str(value['age']):
number += 1 # 优先值+1
else:
continue
if kwargs['class_number']: # 判断用户是否输入了班级
if kwargs['class_number'] in str(value['class_number']):
number += 1 # 优先值+1
else:
continue
if kwargs['sex']: # 判断用户是否输入了性别
if kwargs['sex'] in str(value['sex']):
number += 1 # 优先值+1
else:
continue
if number == 0:
continue
compare.append(number)
compare.sort(reverse=True)
result.insert(compare.index(number), {key: value}) # 会按照对应的信息个数从大到小进行排序
if not result:
print('未搜索到相关信息,或相关信息不符合')
return result
@staticmethod
def set_info():
print('请输入要查找、设置或更改后的学生信息:')
name1 = input('请输入学生姓名:')
age1 = int(input('请输入学生年龄:'))
class_number1 = int(input('请输入学生班级:'))
sex1 = input('请输入学生性别(male/famale):')
return dict(name=name1, age=age1, class_number=class_number1, sex=sex1)
# 用户选择的服务
def server(self):
n = True
while n:
print('==' * 20)
print('请选择服务:')
print('1.查看所有学生信息')
print('2.添加学生信息')
print('3.批量添加学生信息')
print('4.修改学生信息')
print('5.批量修改学生信息')
print('6.删除学生信息')
print('7.批量删除学生信息')
print('8.模糊查找学生信息')
print('9.按学号查找学生信息')
print('10.退出学生信息管理系统')
try:
choose = int(input('请输入服务序号:'))
if choose > 10:
raise OrderNumberError('服务序号不存在')
try:
if choose == 1:
self.get_all()
print('==' * 20)
except AttributeError:
print('当前库中无成员~')
if choose == 2:
ii = self.set_info() # ii == input_info代表输入的信息
self.add(**ii)
print('添加成功~,添加的学生信息如下:')
print(f'学号{max(self.students)}:{self.students[max(students)]}')
print('==' * 20)
if choose == 3:
result = []
condition = True
while condition:
infos = self.set_info()
result.append(infos)
choose_exit = input('是否继续添加?y/n:').lower()
if choose_exit == 'y':
continue
elif choose_exit == 'n':
self.adds(result)
print('添加成功!添加的全部信息如下~:')
for i in result:
# range 用于绑定学号与学生
for items in self.students:
if self.students[items] == i:
print(f'学号:{items}: {i}')
while True:
sure = input('以上信息是否有误?(y/n):').lower()
if sure == 'y':
print('即将进入修改界面~')
frequency = 0
while frequency <= 3:
try:
students_id = int(input('请输入所要更改学生的学号:'))
print('设置学生信息:')
ii1 = self.set_info() # ii == input_info代表输入的信息
self.update(students_id, **ii1)
except ValueError:
print(f'请输入正确格式的学号!(输错3次即强制退出,目前{frequency}次)')
frequency += 1
print('==' * 20)
elif sure == 'n':
condition = False
break
else:
print('error!!!请输入y/n:')
else:
print('error!!!请输入y/n:')
if choose == 4:
id_number = int(input('请输入已存在的学号:'))
print('学号-{{{}}}-,对应的学生信息如下:\n{}'.format(id_number, self.students[id_number]))
print('请选择要更改的学生信息:')
print('1.学生姓名')
print('2.学生年龄')
print('3.学生班级')
print('4.学生性别')
number = int(input('请输入需要更改的序号:'))
if number == 1:
name1 = input('请输入学生姓名:')
self.students[id_number]['name'] = name1
if number == 2:
age1 = int(input('请输入学生年龄:'))
self.students[id_number]['age'] = age1
if number == 3:
class_number1 = int(input('请输入学生班级:'))
self.students[id_number]['class_number'] = class_number1
if number == 4:
sex1 = input('请输入学生性别(male/famale):')
self.students[id_number]['sex'] = sex1
print('更新后的学生信息如下:')
print(self.students[id_number])
print('==' * 20)
if choose == 5:
frequency = 0
info_list = []
_id_list1 = []
while frequency <= 3:
try:
print('即将进入修改界面~')
print('请更改学生信息:')
student_id = int(input('请输入学号(可多次):'))
_id_list1.append(student_id) # 将需要修改的学生的学号传入列表中,以便更新结束返回信息
ii1 = self.set_info()
_set = {student_id: ii1} # ii == input_info代表输入的信息
info_list.append(_set)
choose_exit = input('是否继续添加?y/n:').lower()
if choose_exit == 'y':
continue
elif choose_exit == 'n':
self.updates(info_list)
print('您已退出~')
print('您修改后的信息如下:')
for i in _id_list1:
print(self.students[i])
print('==' * 20)
break
else:
print('error!!!请输入y/n')
except ValueError:
print(f'请输入正确格式的学号!(输错3次即强制退出,目前{frequency}次)')
frequency += 1
if choose == 6:
id_number = int(input('请输入要删除学生的学号:'))
self.delete(id_number)
print('==' * 20)
if choose == 7:
frequency = 1
_id_list2 = []
while frequency <= 3:
try:
student_id = int(input('请输入学号(可多次)'))
_id_list2.append(student_id)
choose_exit = input('是否继续添加?y/n:').lower()
if choose_exit == 'y':
continue
elif choose_exit == 'n':
print('您已退出~')
print('您删除的学生信息如下:')
for i in _id_list2:
print(self.students[i])
print('==' * 20)
self.deletes(_id_list2)
break
else:
print('error!!!请输入y/n:')
except ValueError:
print(f'请输入正确格式的学号!(输错3次即强制退出,目前{frequency}次)')
frequency += 1
if choose == 8:
print('(下列信息,如某一项不知晓请输入\"0\")')
ii2 = self.set_info()
for d in list(ii2.keys()):
if ii2[d] == 0 or ii2[d] == '0':
ii2[d] = None
print('查找到的学生信息如下:')
for u in self.search(**ii2):
for _id, value in list(u.items()):
print('学号{},姓名{},年龄{},班级{},性别{}'.format(_id, value['name'], value['age'], value['class_number'], value['sex']))
if choose == 9:
frequency = 1
while frequency <= 3:
try:
stu_id = int(input('请输入您所要查找的学生的学号:'))
print(self.get_by_id(stu_id))
break
except ValueError:
print(f'请输入正确格式的学号!(输错3次即强制退出,目前{frequency}次)')
frequency += 1
if choose == 10:
print('您已退出~')
print('==' * 20)
break
except OrderNumberError as e:
print(e)
if __name__ == '__main__':
while True:
# 打开文件
file = open('储存学生信息.pickle', 'rb+')
_context = file.read()
# 判断student内是否有数据,若没有数据则往里面加入一个大括号,以便让eval函数识别出文件内的数据是个字典对象,以便程序的顺利进行。
'''
file.read() 方法在读取文件内容后,文件指针会移动到文件的末尾。这意味着后续对文件的读取操作将无法获取到文件的内容,因为已经到达了文件末尾。
如果你需要多次读取文件的内容,可以将读取的结果保存在变量中,以便后续重复使用,而不是每次都调用 file.read()。
'''
if _context:
# 将txt文件中的数据载入,并把阅读出来的json字符串转化为python相应的数据类型
students = pickle.loads(_context)
break
else:
# 若打开的是一个空文件,则在文件中加入一个‘{}’,用来反序列化后被识别为一个字典
file.write(pickle.dumps('{}'))
# 创建Studentinfo类的一个实例, 并把students实际参数传入类中
students_info = StudentsInfo(students)
# 调用类中的server方法
students_info.server()
# 关闭文档
file.close()
# 以模式打开文件
file = open(r'D:\pythonlearn\01-阶段一:python基础入门\第04周 python面向对象与异常处理机制\储存学生信息.pickle',
'wb')
file.seek(0)
# 将程序执行结束后的变量传入所打开的文档中
file.write((pickle.dumps(students_info.students)))
# 关闭文档以保存数据
file.close()