需求如下:创建管理员、教师、学员这三个视图,实现一个简单的课程操作交互
具体实现如下:
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\')
演示如下: