一个简单的多用户交互系统的实现
需求如下:创建管理员、教师、学员这三个视图,实现一个简单的课程操作交互
具体实现如下:
Homework:
│
├─bin
│──────start.py #程序的入口
│
├─conf
│──────config.py #程序用到的文件的路径以及其他关系映射信息
│
├─core #
│──────logger.py#记录日志的逻辑
│──────main.py#实现三类用户的登陆逻辑,并利用反射调用不同类的具体方法
│──────manager.py#实现了管理员类的各个功能
│──────my_pickle.py#实现了将不同对象dump、load进文件的方法以及修改已经被dump的文件的方法
│──────other_logics.py#其他额外功能的逻辑
│──────school.py#里面的classes类用来创建与班级名相同名字的文件
│──────student.py#实现学生类的功能逻辑
│──────teacher.py#实现讲师类的功能逻辑
│
└─db
│──classes_obj#存放各个班级对象的信息
│──logs.log#存放日志信息
│──teacher_obj#存放讲师对象信息
│──userinfo#存放登陆用户信息
│
└─studentinfo#里面的文件是与班级同名的文件,各个文件里面存的是相应班级里的学员信息
│────────python_s9
代码如下:
from os import getcwd,path from sys import path as sys_path sys_path.insert(0,path.dirname(getcwd())) from core import main if __name__ == '__main__': main.main()
import os PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #userinfo文件的路径 USERINFO = os.path.join(PATH,'db','userinfo') #schoolinfo的路径 SCHOOLINFO = os.path.join(PATH,'db','school_obj') #teacher_obj的路径 TEACHER_OBJ = os.path.join(PATH,'db','teacher_obj') #classes_obj的路径 CLASSES_OBJ = os.path.join(PATH,'db','classes_obj') #course_obj的路径 COURSE_OBJ = os.path.join(PATH,'db','course_obj') #studentinfo的路径 STUDENTINFO = os.path.join(PATH,'db','studentinfo') #日志文件的路径 LOGGER = os.path.join(PATH,'db','logs.log') #用户选择视图输入项与身份信息的键值对 USER_DICT = {'1':'Manager','2':'Teacher','3':'Student'} #学校与课程对应关系字典 school_class = {'Beijing':['python','linux'],'Shanghai':['go']}
import logging from conf import config def logger_file(): #生成logger对象 whw_logger = logging.getLogger('logs.log') whw_logger.setLevel(logging.INFO) #生成handler对象 whw_fh = logging.FileHandler(config.LOGGER) whw_fh.setLevel(logging.INFO) #生成Formatter对象 file_formatter = logging.Formatter(' %(asctime)s - %(name)s - %(levelname)s - %(message)s ') #把formatter对象绑定到handler对象中 whw_fh.setFormatter(file_formatter) # 把handler对象绑定到logger对象中 whw_logger.addHandler(whw_fh) return whw_logger def write_log(msg): log_obj = logger_file() log_obj.info(msg) log_obj.handlers.pop()
import sys import os from core.manager import Manager from core.teacher import Teacher from core.student import Student from conf import config from core import my_pickle from .other_logics import file_name,show_classes,show_student_score def login(): ''' 登录函数,应该先到conf.config中先读取userinfo的文件路径。 再读取userinfo文件中的信息,对用户名与密码进行查验 登陆成功后查看这个人的身份来确定进入哪一个视图 :return: ''' while 1: print( '\033[1;32m请选择用户视图:\n\033[0m', '1:管理员视图\n', '2:讲师视图\n', '3:学生视图\n' ) choice = input('请选择相应视图的编号:') if not choice.isdigit() or int(choice)<=0 or int(choice)>3: print('\033[1;31m请输入正确的编号!\033[0m') continue else: username = input('用户名:') password = input('密 码:') user_identity = config.USER_DICT[choice] #打开userinfo文件... with open(config.USERINFO,'r',encoding='utf-8') as f: for line in f: user_name,pass_word,identity = line.strip().split('|') #必须是用户名 密码 身份 三个全部一致才能登陆成功 if user_name == username and password == pass_word and user_identity == identity: print('\033[1;32m%s 用户 %s 登陆成功!\033[0m' % (user_identity,username)) #以字典形式返回用户名 身份 return {'username':username,'identity':identity} else: print('\033[1;31m抱歉,输入有误,登陆失败!\033[0m') def main(): ''' 打印欢迎信息 调用login()——得到一个返回值:用户的姓名与身份 打印用户身份的功能菜单 如果用户想要调用任何方法,应该通过角色的对象调用,跳转到对应对象的方法里 :return: ''' print('\033[0;35m欢迎进入学生选课系统!\033[0m') ret = login() if ret: while 1: #管理员登陆 if ret['identity'] == 'Manager': print('\033[0;32m******************\033[0m') role_class = getattr(sys.modules[__name__],ret['identity']) #将类实例化成相应的对象 obj = role_class(ret['username']) while 1: print('\033[0;32m******************\033[0m') for i,v in enumerate(role_class.menu,1): print(i,v[0]) #避免输入不合法,利用异常处理 choice = input('\033[0;32m请输入您要进行的操作编号:\033[0m') if not choice.isdigit(): print('\033[1;31m请输入数字!\033[0m') break choice_int = int(choice) if choice_int < 1 or choice_int > 9: print('\033[1;31m抱歉,请输入正确的编号!\033[0m') break ##进行第二次的反射,后面加了括号直接执行了 getattr(obj,role_class.menu[choice_int-1][1])() #讲师登陆 if ret['identity'] == 'Teacher': print('\033[0;32m******************\033[0m') global teacher_school #反射:从本模块找到Teacher类 teacher_class = getattr(sys.modules[__name__],ret['identity']) pk_teacher = my_pickle.MyPickle(config.TEACHER_OBJ) ob_teacher = pk_teacher.loaditer() for i in ob_teacher: if i.name == ret['username']: teacher_school = i.school #利用找到的Teacher类与用户输入的name、school实例化讲师对象 teacher_obj = teacher_class(ret['username'],teacher_school) while 1: print('\033[0;32m******************\033[0m') for i,v in enumerate(teacher_class.menu,1): print(i,v[0]) choice = input('\033[0;32m请输入您要进行的操作编号:\033[0m') if choice.isdigit() and 0 < int(choice) < 5: choice_int = int(choice) #第二次反射,后面加了括号,直接执行就行 getattr(teacher_obj,teacher_class.menu[choice_int-1][1])() else: print('\033[1;31m抱歉,请输入正确的编号!\033[0m') #学生登录——需要输入班级 if ret['identity'] == 'Student': global stu_status stu_status = False global stu_class print('\033[0;32m******************\033[0m') class_input = input('请输入您所在的班级:').strip() #判断输入的班级是否存在 if class_input not in file_name(config.STUDENTINFO): print('\033[1;31m抱歉,请输入正确的班级!\033[0m') else: #判断输入的班级有没有登陆的学生name pk_student = my_pickle.MyPickle(os.path.join(config.STUDENTINFO,class_input)) obj_student = pk_student.loaditer() for i in obj_student: #找到了这个学生对象 if i.name == ret['username']: #将这个班级赋值给global变量stu_class stu_class = i.clas stu_status = True #说明成功找到了该学生的对象 if stu_status == True: # 第一次反射 student_class = getattr(sys.modules[__name__], ret['identity']) #实例化 student_obj = student_class(ret['username'],stu_class) while 1: print('\033[0;32m******************\033[0m') for i,v in enumerate(student_obj.menu,1): print(i,v[0]) choice = input('请输入您要进行的操作编号:') if choice.isdigit() and ( 0 < int(choice) < 3): if choice == '1': show_student_score(ret['username'],class_input) elif choice == '2': exit() else: print('\033[1;31m请输入正确的操作编号!\033[0m') else: print('\033[1;31m抱歉您不在这个班级!\033[0m') else: print('\033[1;31m请输入正确的用户编号\033[0m')
#首先,以管理员的身份登录 #登录后 应该实例化一个对应身份的对象manager_obj = manager(name) #管理员对象可以调用所有的方法 from conf import config from core import teacher from core import my_pickle from core import student from core import school from .other_logics import file_name from .logger import logger_file from .logger import write_log import os #管理员类 class Manager: menu =[ ('创建讲师账号','create_teacher'),('创建学生账号','create_student'), ('创建班级', 'create_classes'), ('查看学校','show_school'), ('查看课程','show_courses'),('查看讲师','show_teachers'), ('查看班级','show_classes'),('为班级指定老师','bound_class_teacher'), ('退出','exit') ] def __init__(self,name): self.name = name #相当于拿到了一个对象,这个对象里面只存了文件的名字 self.teacher_pickle_obj = my_pickle.MyPickle(config.TEACHER_OBJ) #拿到 mypickle_obj self.school_pickle_obj = my_pickle.MyPickle(config.SCHOOLINFO) self.classes_pickle_obj = my_pickle.MyPickle(config.CLASSES_OBJ) self.course_pickle_obj = my_pickle.MyPickle(config.COURSE_OBJ) def exit(self): exit() #往文件里面添加内容的时候写成统一的方法 #因为它既没有引用类的属性,也没有引用对象的属性,所以把它做成非绑定方法 @staticmethod def userinfo_handler(content): with open(config.USERINFO,'a') as f: f.write('\n%s' % content) def show(self,pickle_obj): #反射 pick_obj = getattr(self,pickle_obj) #执行一个生成器函数就拿到一个生成器对象,这里拿到的每一个值,都是生成器返回的对象 load_g = pick_obj.loaditer() for course_obj in load_g: for i in course_obj.__dict__:#### print('%s: %s'%(i,course_obj.__dict__[i])) print('*' * 20) def show_school(self): print('校区信息如下:') for i,v in enumerate(config.school_class,1): print('%s: %s'% (i,v)) def show_teachers(self): self.show('teacher_pickle_obj') def show_courses(self): print('课程信息如下:') for i,v in enumerate(config.school_class): print('学校:%s-->课程:%s' % (v,config.school_class[v])) def show_classes(self): self.show('classes_pickle_obj') def create_teacher(self): l = [] with open(config.USERINFO,'r') as f: for line in f: username,v,b = line.strip().split('|') l.append(username) teacher_name = input('请输入老师的姓名:') if teacher_name in l: print('\033[1;31m该讲师已经存在!\033[0m') return teacher_pass = input('请输入老师的密码:') self.show_school() teacher_school = input('请输入所属的学校:(Beijing|Shanghai)') #存入文件 content = '%s|%s|Teacher' % (teacher_name,teacher_pass) Manager.userinfo_handler(content) #根据输入的姓名与学校值实例化一个老师对象 teacher1 = teacher.Teacher(teacher_name,teacher_school)###实例化 self.teacher_pickle_obj.dump(teacher1) print('\033[0;32m讲师创建成功!\033[0m') write_log('创建了讲师:%s' % teacher_name) def create_classes(self): #注意,新建的班级名字不能是已经存在的 classes_names = file_name(config.STUDENTINFO) print('\033[0;32m已经存在的班级如下:\033[0m') for i,v in enumerate(classes_names,1): print('%s: %s' % (i,v)) class_name = input('请输入您要新建的班级名称:') if class_name in classes_names: print('\033[1;31m该班级已经存在!\033[0m') return school_name = input('请输入所属的学校:(Beijing|Shanghai)') if school_name not in 'Beijing|Shanghai'.split('|'): print('\033[1;31m请输入正确的学校!\033[0m') return course_name = input('请输入课程名称:(python|linux|go)') if course_name not in 'python|linux|go'.split('|'): print('\033[1;31m请输入正确的课程!\033[0m') return #判断 Beijing只能有python与linux,Shanghai只能有go: if (school_name == 'Beijing' and (course_name =='python' or course_name == 'linux')) or (school_name == 'Shanghai' and course_name == 'go'): #如果符合条件的话新建一个路径 student_path = os.path.join(config.STUDENTINFO,class_name) #利用上面的路径创建一个空文件 open(student_path,'w').close() class_obj = school.Classes(school_name,class_name,course_name) #利用pickle dump实例化的对象,这一点json做不到! self.classes_pickle_obj.dump(class_obj)####### print('课程 %s 创建成功!' % course_name) write_log('创建了课程:%s' % course_name) else: print('\033[1;31m您填写的学校与课程的关系不存在!\033[0m') return def create_student(self): student_name = input('请输入学生姓名:') student_pass = input('请输入学生密码:') #列举出所有存在的班级 classes_names = file_name(config.STUDENTINFO) print('\033[0;32m已经存在的班级如下:\033[0m') for i, v in enumerate(classes_names, 1): print('%s: %s' % (i, v)) student_class = input('请输入学生所在的班级:') class_g = self.classes_pickle_obj.loaditer() for clas in class_g: #这里有既有班级名卡控,不用做判断了 if clas.name == student_class: #先把用户名、密码、角色写入userinfo文件中 content = '%s|%s|Student' % (student_name,student_pass) Manager.userinfo_handler(content) #实例化 stu_obj = student.Student(student_name,clas) student_path = os.path.join(config.STUDENTINFO,student_class) my_pickle.MyPickle(student_path).dump(stu_obj) print('学生 %s 创建成功!' % student_name) write_log('学生 %s 创建成功!' % student_name) return else: print('\033[1;31m输入错误,创建学生失败!\033[0m') #for...else 语句 def bound_class_teacher(self): global a global status status = False #显示既有的班级 classes_names =file_name(config.STUDENTINFO) print('\033[0;32m已经存在的班级如下:\033[0m') for i, v in enumerate(classes_names, 1): print('%s: %s' % (i, v)) class_name = input('请输入要指定的班级:') #判断一下输入的班级是否存在 if class_name not in classes_names: print('\033[1;31m抱歉,没有这个班级!\033[0m') return #显示既有讲师 print('所有讲师信息为:') self.show_teachers() teacher_name = input('请输入需要指定的讲师:') #利用teacher_obj文件实例化出一个可迭代的对象 teach_g = self.teacher_pickle_obj.loaditer() #遍历这个可迭代的对象 for teacher_obj in teach_g: # 前提是姓名是唯一的,找到了对应的老师! if teacher_obj.name == teacher_name: if class_name in teacher_obj.classes: print('\033[1;31m本教师已经与该课程有了绑定关系,请勿重复绑定!\033[0m') return #将符合要求的对象赋值给一个global变量,待后面处理 teacher_obj.classes.append(class_name) a = teacher_obj #判断成功修改 status = True #必须等遍历完文件关闭后才能再进行edit操作 if status == True: file1 = my_pickle.MyPickle(config.TEACHER_OBJ) file1.edit(a) print(a.name,a.classes) print('绑定成功!') write_log('班级%s绑定了讲师:%s' % (class_name,teacher_name)) a = None status = False else: print('\033[1;31m录入有误,绑定失败!\033[0m')
import pickle import os class MyPickle: def __init__(self,filename): #只是用文件名来实例化对象,并没有打开文件 self.filename = filename #每次需要dump一个文件的时候需要先打开一个文件 def dump(self,obj): with open(self.filename,'ab') as f: # 利用pickle dump实例化的对象,这一点json做不到! pickle.dump(obj,f) #每次需要load文件的时候,需要转格式 def loaditer(self): with open(self.filename,'rb') as f: while 1: try: #不能把所有的文件都同时读出来,需要每读一个文件再做一次操作 obj = pickle.load(f) yield obj except: break def close_file(): f.close() #"修改"已经被dump的文件 def edit(self,obj): #利用原文件.bak这个路径名实例化一个新的对象,然后利用dump跟loaditer方法将满足条件的信息写进这个bak文件中,最后replace~~ f_temp = MyPickle(self.filename+'.bak') with open(self.filename,'rb+') as f: for i in self.loaditer(): if i.name == obj.name: f_temp.dump(obj) else: f_temp.dump(i) os.replace(self.filename+'.bak',self.filename)
import os from conf import config from .my_pickle import MyPickle # 查找studentinfo文件夹下文件列表的方法 def file_name(file_dir): for root, dirs, files in os.walk(file_dir): return files # 显示etudentinfo文件夹下所有文件名的方法 def show_classes(): for root, dirs, files in os.walk(config.STUDENTINFO): for i, v in enumerate(files, 1): print('%s : %s' % (i, v)) # 因为学生需要班级来实例化,而班级是一个个的文件,所以显示学生成绩的方法写在函数里 def show_student_score(name, file): class_path = os.path.join(config.STUDENTINFO, file) pk_stu = MyPickle(class_path) obj_stu = pk_stu.loaditer() for i in obj_stu: if i.name == name: print('%s 的成绩为:%s' % (name, i.score)) return
class Classes: def __init__(self,school,name,course): self.school = school self.name = name #班级名称,, self.course = course #科目 python go linux #self.student_path = student_path #学生信息文件的绝对路径
class Student: menu = [('查看自己的成绩:','show_my_score'), ('退出','exit') ] def __init__(self,name,clas): self.name = name self.clas = clas self.score = ''
from core import my_pickle from conf import config import os # class Classes: # def __init__(self,school_name,class_name,class_kind): # self.school_name = school_name #分校 # self.class_name = class_name #班级名 python_s11 # self.class_kind = class_kind #班级科目 python go linux # self.student = ['student_obj'] class Course: def __init__(self,course_name,course_period,course_price): self.course_name = course_name self.course_period = course_period#周期 self.course_price = course_price class Teacher: menu = [ ('查看本人所授班级','show_classes'), ('查看所授班级学生','show_class_students'), ('修改学生成绩','reverse_grade'), ('退出','exit') ] def __init__(self,name,school): self.name = name self.school = school self.classes = [] def exit(self): exit() def show_classes(self): print('\033[1;32m您所教授的班级为:\033[0m') global classes_list #利用teacher_obj文件实例化一个MyPickle对象 teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ) #利用loaditer得到一个可迭代的对象 teacher_obj = teacher_file.loaditer() #遍历~ for i in teacher_obj: #找到对应的讲师 if i.name == self.name: #班级列表赋值给classes_list classes_list = i.classes #显示这个classes_list列表,就是这个老师教的班级 for i,v in enumerate(classes_list,1): print('%s: %s' % (i,v)) return def show_class_students(self): clas = input('请输入您要查找学生所在的班级:').strip() global classes_list1 # 利用teacher_obj文件实例化一个MyPickle对象 teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ) # 利用loaditer得到一个可迭代的对象 teacher_obj = teacher_file.loaditer() # 遍历~ for i in teacher_obj: # 找到对应的讲师 if i.name == self.name: # 班级列表赋值给classes_list classes_list1 = i.classes #判断输入的班级是否在老师所教班级的列表里 if clas in classes_list1: #得到这个班级文件的路径 class_path = os.path.join(config.STUDENTINFO,clas) #利用这个班级文件路径再实例化一个MyPickle对象... pk_class = my_pickle.MyPickle(class_path) class_obj = pk_class.loaditer() for i in class_obj: print('学生姓名:%s;学生成绩:%s' % (i.name,i.score)) else: print('\033[1;31m抱歉,您没有教这个班级!\033[0m') def reverse_grade(self): self.show_classes() clas = input('请输入您要修改的学生所在的班级:').strip() global classes_list1 global stu1 global s_status s_status = False # 利用teacher_obj文件实例化一个MyPickle对象 teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ) # 利用loaditer得到一个可迭代的对象 teacher_obj = teacher_file.loaditer() # 遍历~ for i in teacher_obj: # 找到对应的讲师 if i.name == self.name: # 班级列表赋值给classes_list classes_list1 = i.classes # 判断输入的班级是否在老师所教班级的列表里 if clas in classes_list1: # 得到这个班级文件的路径 class_path = os.path.join(config.STUDENTINFO, clas) # 利用这个班级文件路径再实例化一个MyPickle对象... pk_class = my_pickle.MyPickle(class_path) class_obj = pk_class.loaditer() choice_stu = input('请输入您要修改成绩的学生:').strip() score = input('请输入该学生修改后的成绩:').strip() for i in class_obj: if i.name == choice_stu: i.score = score #将这个学生对象赋值给global变量stu1 stu1 = i s_status = True else: print('\033[1;31m抱歉,您没有教这个班级!\033[0m') return if s_status == True: stu_file = my_pickle.MyPickle(os.path.join(config.STUDENTINFO,clas)) stu_file.edit(stu1) print('学生:%s;成绩:%s' % (stu1.name,stu1.score)) print('\033[1;32m修改成功!\n\033[0m') s_status = False else: print('\033[1;31m录入有误,操作失败!\033[0m')
演示如下: