项目文件:课程选课系统
开发环境:PyCharm 2021.1.1

文章目录

  • 一、任务描述及数据
    • 1.1 要求
  • 二、需求分析及实现路线
    • 2.1 管理视图
    • 2.2 学员视图
    • 2.3 讲师视图
  • 三、模块设计及实现
    • 3.1 视图层
    • 3.2 逻辑接口层
    • 3.3 数据处理层
    • 3.4 角色类的设计
    • 3.5 登录功能设计
    • 3.6 数据存放格式
    • 3.7 程序结构图
  • 四、测试结果
    • 4.1 管理员功能
    • 4.2 教师功能
    • 4.3 学生功能
  • 五、总结
  • 六、参考文献

一、任务描述及数据

主要任务:三人自由组合完成一个课程系统

角色: 学校、学员、课程、讲师、管理员

1.1 要求

  1. 创建武汉、长沙 2 所学校 (管理员创建学校)
  2. 创建 Linux、Python、C 语言 3 门课程 , Linux、Python 在武汉开课, C 语言在长沙开课
  3. 课程包含:周期、价格(通过学校创建课程)
  4. 提供两个角色接口:创建讲师、创建学员时需选择学校,关联班级
  5. 学员视图,可以注册,选择课程(等同于选择班级)
  6. 讲师视图,讲师可管理自己的课程,上课时选择班级,查看班级学员列表 , 修改所管理的学员的成绩
  7. 管理视图,创建讲师, 创建班级,创建课程
  8. 上面的操作产生的数据都通过pickle序列化保存到文件里(pickle 可以帮我们保存对象)

二、需求分析及实现路线

2.1 管理视图

  1. 注册

    (1)用户在视图层输入用户名与密码交给接口层

    (2)接口层调用数据层的 models.select 进行校验

    (3)若不存在,则创建,并将注册成功返回给用户视图层

  2. 登录

    (1)用户在视图层输入用户名与密码交给接口层

    (2)接口层调用数据层的 models.select 进行校验

    (3)若存在则校验密码,并将登录成功返回给用户视图层

  3. 创建学校

    (1)让用户输入学校名与学校地址

    (2)调用管理员创建学校接口

    (3)判断学校是否存在,若存在,不让创建

    (4)若不存在,则调用接口层创建学校,获取对象的创建学校方法,保存学校

    (5)将结果返回给视图层

  4. 创建课程(先选择学校)

    (1)获取所有学校并打印,让用户选择

    (2)获取用户选择的学校与创建的课程交给接口层

    (3)接口层调用管理员对象中的创建课程方法,保存课程对象

    (4)课程需要绑定给学校对象,最终将创建成功的结果返回给视图层

  5. 创建讲师

    (1)让用户输入老师的名称

    (2)调用接口层,接口层中设置默认密码 123,调用数据层

    (3)判断老师是否存在,不存在则调用管理员对象中的创建老师方法

    (4)保存老师对象并将结果返回给视图层

2.2 学员视图

  1. 注册

  2. 登录

  3. 选择校区

    (1)获取所有学校让学生选择,将选择的学校传给接口层

    (2)接口层判断当前学生是否选择学校

    (3)若没有选择,则调用学生对象中的添加学校方法

    (4)将添加后的消息返回给视图层

  4. 选择课程(先选择校区,再选择校区中的某一门课程),学生与课程互相选择

    (1)先获取当前学生所在学校的所有课程并选择

    (2)接口层将选择后的学生课程,调用数据层的添加课程方法,保存

    (3)学生对象中的课程列表添加该课程,设置课程分数默认为 0

    (4)最终将结果返回给视图层

  5. 查看分数

    (1)直接调用接口层

    (2)接口层调用数据层的查看成绩方法

    (3)返回成绩给视图层并打印

2.3 讲师视图

  1. 登录

  2. 查看授课课程

    (1)直接调用接口层,获取老师对象下课程列表数据

    (2)若有则打印,没有则退出

  3. 选择教授课程

    (1)调用接口层中的选择教授课程接口,调用数据层中该课程下所有的学生返回给视图层

    (2)打印所有的课程让老师选择,若老师课程中有该课程则不让添加

    (3)没有则调用老师中的添加课程方法进行添加

  4. 查看课程下的学生名单

    (1)直接获取老师对象下所有的课程,选择课程

    (2)从老师对象中,调用查看课程下学生方法,获取课程对象下所有的学生,返回给视图层

    (3)视图层打印,该课程下所有的学生

  5. 修改学生分数

    (1)直接获取老师对象下所有的课程,选择课程

    (2)从老师对象中,调用查看课程下学生方法,获取课程对象下所有的学生,返回给视图层
    (3)视图层打印,该课程下所有的学生,并让用户选择需要修改分数的学生
    (4)调用老师修改分数接口,获取老师对象,调用对象中的修改分数方法
    (5)获取学生对象中的 score_dict 分数字典进行修改

三、模块设计及实现

实现思路:项目采用三层架构设计,基于面向对象封装角色数据和功能。面向过程和面向对象搭配使用。程序开始,用户选择角色,进入不同的视图层,展示每个角色的功能,供用户选择。进入具体角色视图后,调用功能,对接逻辑接口层获取数据并展示给用户视图层。逻辑接口层需要调用数据处理层的类,获取类实例化对象,进而实现数据的增删改查。

3.1 视图层

用于与用户进行交互,如小的逻辑判断,比如注册功能中两次密码是否一致的校验

core:src.py(主视图)、admin.py(管理员视图)、student.py(学生视图)、teacher.py(教师视图)

视图层封装成视图类

之所以想要将视图层封装成视图类,主要是为了简化代码和避免手动编写用户的功能函数字典。采用视图类之后,可以将功能函数做成视图类的对象的绑定方法,采用反射,可以自动获取并调用。但这里需要做一个处理:用户选择角色后,如何获取并显示这个角色的功能函数函数列表?这里需要在视图类里面做一个显示功能的方法start,这个方法要在用户选择先显示所有的功能,在此之前,还需要一个收集角色功能的方法auto_get_func_menu,这个函数必须在对象使用时就立即工作,最后,还要配合一个装饰器my_func,让收集函数知道搜集那些功能,保存下来func_list,让显示函数获取。上述这个过程涉及的方法是每个视图类都要有的,因此抽象出来一个基础视图类BaseViewer。最后,视图类需要用到一些公用工具(lib/tool.py),将它封装成一个ToolsMixin类,视图类继承之,方便传参。

# BaseView.py
from functools import wrapsclass BaseViewer:name = Nonerole = Nonefunc_list = []     # 存放角色功能方法def __init__(self):self.auto_get_func_menu()  # 初始化就启动,搜集角色功能方法def auto_get_func_menu(self):"""自动调用功能函数触发装饰器的执行,将功能函数添加到类属性 func_list中:return:"""not_this = ['auto_get_func_menu', 'my_func', 'start']all_funcs = {k: v for k, v in self.__class__.__dict__.items()if callable(v) and not k.startswith('__') and k not in not_this}for func in all_funcs.values():func()def start(self):"""开始函数,功能菜单显示,供管理员选择:return:"""while 1:for index, func_name in enumerate(self.func_list, 1):print('tttttt', index, func_name[0], sep='t')choice = input('>>>(Q退出):').strip().lower()if choice == 'q':self.func_list.clear()breakif not choice.isdigit() or int(choice) not in range(1, len(self.func_list) +1):print('编号不存在, 请重新输入')continuefunc = self.func_list[int(choice)  1][1]func(self)@staticmethoddef my_func(desc):"""装饰器,实现功能函数自动添加到类的func_list中:return:"""def wrapper(func):@wraps(func)def inner(*args, **kwargs):BaseViewer.func_list.append((desc, func))return innerreturn wrapper@staticmethoddef auth(role):"""装饰器,登录校验:return:"""def wrapper(func):@wraps(func)def inner(*args, **kwargs):if BaseViewer.name and BaseViewer.role == role:res = func(*args, **kwargs)return reselse:print('您未登录或没有该功能的使用权限')return innerreturn wrapperdef login(self, role_interface):while 1:print('登录页面'.center(50, ''))name = input('请输入用户名(Q退出):').strip().lower()if name == 'q':breakpwd = input('请输入密码:').strip()if self.is_none(name, pwd):print('用户名或密码不能为空')continueflag, msg = role_interface.login_interface(name, self.hash_md5(pwd))print(msg)if flag:BaseViewer.name = namebreak# 学生视图 student.py
from core.baseview import BaseViewer as Base
from lib.tools import ToolsMixin
from interface import student_interface, common_interfaceclass StudentViewer(ToolsMixin, Base):@Base.my_func('登录')def login(self):Base.role = 'Student'super().login(student_interface)@Base.my_func('选择课程')@Base.auth('Student')def select_course(self):while 1:school_name = student_interface.get_my_school_interface(self.name)flag, course_list = common_interface.get_course_list_from_school(school_name)if not flag:print(course_list)breakprint('待选课程列表'.center(30, ''))flag2, course_name = self.select_item(course_list)if not flag2:breakflag3, msg = student_interface.select_course_interface(course_name, self.name)print(msg)@Base.my_func('我的课程')@Base.auth('Student')def check_my_course(self):flag, course_list = student_interface.check_my_course_interface(self.name)if not flag:print(course_list)returnprint('我的课程:'.center(30, ''))for index, course_name in enumerate(course_list, 1):print(index, course_name)@Base.my_func('我的分数')@Base.auth('Student')def check_my_score(self):flag, score_dict = student_interface.check_score_interface(self.name)if not flag:print(score_dict)else:print('课程分数列表')for index, course_name in enumerate(score_dict, 1):score = score_dict[course_name]print(index, course_name, score)@Base.my_func('修改密码')@Base.auth('Student')def edit_my_pwd(self):self.edit_pwd(common_interface.edit_pwd_interface)

3.2 逻辑接口层

核心业务逻辑的处理

interface:admin_interface.py、student_interface.py、teacher_interface.py

3.3 数据处理层

做数据的 CRUD 处理

models.py(存放所有的类:学校类、学院类、课程类、讲师类、管理员类)

db_handler.py(用户保存对象与获取对象,存放 json 格式的数据)

3.4 角色类的设计

从管理员、学生、老师角色中抽象出Human类,有用户基本数据属性和密码相关的公共属性。为了方便角色数据的读取和保存,定义了一个接口类FileMixin,用于对象数据的读取和保存。FileMixin类中设置一个绑定类的方法,这样每个继承FileMixin的类都可以通过对象名判断这个对象的存在与否(多继承时遵循Mixins规范)。对象初始化后立即保存数据,每个功能操作后,也跟一个save_obj方法,这样类的调用起来较为方便。在用户类中设置角色的方法属性,这样直接在逻辑接口层中在获取对象后,直接调用对象的方法即可。这样做是为了保证面向对象的完整性,每个对象都对应其现实意义。

# 对象操作类
class FileMixin:@classmethoddef get_obj(cls, name):return db_handle.get_obj(cls, name)def save_obj(self):db_handle.save_obj(self)# 角色基类 Human
class Human:def __init__(self, name, age, sex):self.name = nameself.age = ageself.sex = sexself.__pwd = settings.INIT_PWDself.role = self.__class__.__name__@propertydef pwd(self):return self.__pwd@pwd.setterdef pwd(self, new_pwd):self.__pwd = new_pwd# 管理员类 Admin
class Admin(FileMixin, Human):def __init__(self, name, age, sex):super().__init__(name, age, sex)self.save_obj()@staticmethoddef create_school(school_name, school_addr):School(school_name, school_addr)@staticmethoddef create_course(school_name, course_name, course_period, course_price):Course(course_name, course_period, course_price, school_name)@staticmethoddef create_teacher(teacher_name, teacher_age, teacher_sex, teacher_level):Teacher(teacher_name, teacher_age, teacher_sex, teacher_level)@staticmethoddef create_student(stu_name, stu_age, stu_sex, school_name, homeland):Student(stu_name, stu_age, stu_sex, school_name, homeland)@staticmethoddef reset_user_pwd(name, role):obj = getattr(sys.modules[__name__], role).get_obj(name)obj.pwd = settings.INIT_PWDobj.save_obj()# 学校类 School
class School(FileMixin):def __init__(self, name, addr):self.name = nameself.addr = addrself.course_list = []self.save_obj()def relate_course(self, course_name):self.course_list.append(course_name)self.save_obj()# 课程类 Course
class Course(FileMixin):def __init__(self, name, period, price, school_name):self.name = nameself.period = periodself.price = priceself.school = school_nameself.teacher = Noneself.student_list = []self.save_obj()def relate_teacher(self, teacher_name):self.teacher = teacher_nameself.save_obj()def relate_student(self, stu_name):self.student_list.append(stu_name)self.save_obj()# 教师类 Teacher
class Teacher(FileMixin, Human):def __init__(self, name, age, sex, level):super().__init__(name, age, sex)self.level = levelself.course_list = []self.save_obj()def select_course(self, course_name):self.course_list.append(course_name)self.save_obj()course_obj = Course.get_obj(course_name)course_obj.relate_teacher(self.name)def check_my_courses(self):return self.course_list@staticmethoddef check_my_student(course_name):course_obj = Course.get_obj(course_name)return course_obj.student_list@staticmethoddef set_score(stu_name, course_name, score):stu_obj = Student.get_obj(stu_name)stu_obj.score_dict[course_name] = int(score)stu_obj.save_obj()# 学生类 Student
class Student(FileMixin, Human):def __init__(self, name, age, sex, school_name, homeland):super().__init__(name, age, sex)self.school = school_nameself.homeland = homelandself.course_list = []self.score_dict = {}self.save_obj()def select_course(self, course_name):self.course_list.append(course_name)self.score_dict[course_name] = Noneself.save_obj()course_obj = Course.get_obj(course_name)course_obj.relate_student(self.name)def check_my_course(self):return self.course_listdef check_my_score(self):return self.score_dict

3.5 登录功能设计

每个角色都有登录需求,因此应抽取一个公用的登录逻辑接口层。不过因为数据存放格式的限制,这里妥协一下。每个登录视图层还是直接调用各自的登录逻辑接口,然后从各自的逻辑接口层中调用公用逻辑接口层的核心登录逻辑判断。这里在角色的登录接口中做一个中转的目的是为了给登录用户设置一个登录角色。并且这个角色的字符串名字和类的名字保持一致,为了方便在公共登录接口中使用反射判断。以下为设计样例:

# admin_interface.py
def login_interface(name, pwd):"""登录接口:param name::param pwd: 密码,密文:return:"""from interface import common_interfacerole = 'Admin'flag, msg = common_interface.common_login_interface(name, pwd, role)return flag, msg# common_interface.py
def common_login_interface(name, pwd, role):"""登录接口:param name::param pwd: 密码,密文:param role: 角色,如,Admin|Teacher|Student:return:"""if hasattr(models, role):obj = getattr(models, role).get_obj(name)if not obj:return False, f'用户名[{name}]不存在'if pwd != obj.pwd:return False, '用户名或密码错误'return True, '登录成功'else:return False, '您没有权限登录'

3.6 数据存放格式

将一个类实例化对象按照类型保存在不同的文件夹中,文件夹名与类名相同,文件名为对象的 name 属性的名字。这样做的好处是方便对象数据的读取和保存,并且对象间没有使用组合的方式,避免数据的重复保存。但这样做也有着不足:每个类下面的对象不能重名。这个问题需要重新组织数据管理方式,让其更实际化。

# json 格式数据存放格式
{school:{course:{'teacher': teacher, 'grade': grade}}}

3.7 程序结构图

四、测试结果

4.1 管理员功能

  1. 注册

  2. 登录

  3. 创建学校

  4. 创建课程

  5. 创建讲师

4.2 教师功能

  1. 教师登录

  2. 教师选择并查看课程

  3. 教师查看课程下学生名单

  4. 教师修改学生分数

4.3 学生功能

  1. 学生注册

  2. 学生登录

  3. 学生选择学校

  4. 学生选择课程

  5. 学生查看分数

五、总结

对于刚学习 python,拿到需求,首先要经过自己分析,确实搞不定了,可以查看一些优秀的代码学习总结归纳,从而转化为自己的能力。对于写程序,实现逻辑思路是很重要的,写项目前一定要先分析需求,再构思设计,最后开始编码。角色设计时,需要全面考虑角色之间的关系方便接口设计。基于反射可以做很多动态判断,避免使用ifelifelse多级判断。三层架构的设计,明确每层职责,系统耦合度低,方便易开发。

六、参考文献

[1] 张艳,顾晨.实践课程网上选课系统的设计与实现[J].实验室研究与探索,2003(03):75-77.

[2] 韩春英. 高校选课系统的设计与实现[D].华东师范大学,2010.

[3] 盛蒙蒙. Python 程序设计课程综合实验案例设计[J]. 现代计算机, 2020, (20):70-73,95.

[Python语言程序设计]课程选课系统相关推荐

  1. lenpython执行结果_哪个选项是下面代码的执行结果? len ( Python 语言程序设计课程 )_学小易找答案...

    [填空题]构成地衣体的真菌绝大部分属于____亚门,少数为____亚门和____亚门的真菌. [单选题]给定字典 d ,哪个选项对 d.get(x, y) 的描述是正确的? [单选题]对于序列 s , ...

  2. 总结Python语言程序设计课程-推荐的Python第三方库

    从数据处理到人工智能 python数据分析方向第三方库有:Numpy, SciPy, Pandas Python数据可视化方向的第三方库有: Seaborn, Matplotblib, Mayavi, ...

  3. 北京理工大学 python专题课程-Python语言程序设计

    Q1:Python语言.C语言.Java语言.VB语言--到底哪种适合作为入门编程语言呢? A1: 如果您是计算机.软件工程.信息类专业学生,毋庸置疑,入门编程语言请学习C语言:如果您是其他专业学生, ...

  4. 中国大学 MOOC 课程Python语言程序设计 (第11期)测试答案(1-5周)

    中国大学 MOOC 课程Python语言程序设计 (第11期)测试答案(1-5周)  Lan   2020-05-03 14:21   369 人阅读  0 条评论 感谢中国大学MOOC提供的学习平台 ...

  5. python语言程序设计嵩天-Python语言程序设计基础(第2版)嵩天课后答案

    嵩天.礼欣.黄天羽Python语言程序设计基础(第2版)习题答案本书提出了以理解和运用计算生态为目标的Python语言教学思想,在系统讲解Python语言语法的同时介绍了从数据理解到图像处理的14个P ...

  6. python北京理工大学推荐的书-Python语言程序设计

    Q1:Python语言.C语言.Java语言.VB语言--到底哪种适合作为入门编程语言呢? A1: Python是最好的程序设计入门语言.也是最先进的程序设计语言. 如果只想学一门程序设计语言,请学P ...

  7. python好学吗mooc中文网-Python语言程序设计

    Q1:Python语言.C语言.Java语言.VB语言--到底哪种适合作为入门编程语言呢? A1: Python是最好的程序设计入门语言.也是最先进的程序设计语言. 如果只想学一门程序设计语言,请学P ...

  8. python从入门到精通 清华大学出版社-清华大学出版社-图书详情-《Python语言程序设计》...

    Python语言由荷兰人Guido van Rossum于1989年发明,1991年首次公开发行.Python语言经过二十多年的发展,已经广泛应用于计算机科学与技术.科学计算.数据的统计分析.移动终端 ...

  9. python语言程序设计是干什么的-Python语言程序设计

    Q1:Python语言.C语言.Java语言.VB语言--到底哪种适合作为入门编程语言呢? A1: Python是最好的程序设计入门语言.也是最先进的程序设计语言. 如果只想学一门程序设计语言,请学P ...

最新文章

  1. sqlserver 类似oracle的rownum功能: row_number
  2. Cmake的install与file命令的区别
  3. MySQL从入门到精通50讲(十)-MySQL中null值如何处理
  4. 洛谷P2401 不等数列(线性DP)
  5. Indesign CS6怎么添加框线_InDesign小小知识库
  6. php中heredoc的使用方法
  7. 星辰大海:华为用“鲲鹏+昇腾”计算双擎再出发
  8. $con=mysql_connect_php连接mysql mysql_connect()与mysqli_connect()的盲点
  9. 微软未来五年将把80%资源投入云计算
  10. 查看sql语句执行时间/测试sql语句性能
  11. win7下没有ntsd提供的程序支持
  12. cmd 查询ip 服务器信息,cmd查看ip(cmd查别人ip)
  13. java PDF添加水印效果
  14. Jmeter-界面功能介绍
  15. Centos7系统安全漏洞及修复方案
  16. 浩辰3D软件中装配零件的操作技巧
  17. 快速上手Flask(一) 认识框架Flask、项目结构、开发环境
  18. spring cloud 总结(摘抄版)
  19. 深入探讨大数据权限管理方案-从哲学到技术
  20. 博客右下角的动态人物(live2d)看板娘

热门文章

  1. UG\NX二次开发 删除所有图层类别 UF_OBJ_delete_object
  2. 粉笔公考——方法精讲——判断推理
  3. mysql数据表名设置大小写不敏感(Linux Centos)
  4. X窗口系统的协议和架构
  5. 多项式拟合lm_数据趋势拟合--多项式拟合
  6. 像素坐标系与图像坐标系
  7. 转发与重定向的区别及应用场景
  8. OLTP和OLAP主要区别
  9. linux中cd /xxx与cd xxx/ 的区别
  10. 高起专计算机应用基础试题及答案,计算机应用基础试题(高起专)..doc