python第三十一课--选课系统代码总结
昨日内容回顾
-
面向对象魔法方法
1.__init__ 对象添加独有数据自动触发(对象实例化方法) 2.__str__ 对象被执行打印操作的时候自动触发 3.__call__ 对象被加括号调用的时候自动触发 4.__getattr__ 对象查找无法使用到的名字自动触发 5.__getattribute__ 对象查找名字就会自动触发 6.__setattr__ 对象执行固定语法 对象.名字 = 值 7.__enter__ 对象被执行with语法开始自动触发 并且返回的数据会给到as后面的变量名 8.__exit__ 对象被执行with语法结束后自动触发 9.__del__ 对象被执行删除(主动 被动)操作的时候自动触发 -
魔法方法笔试题
1.__enter__与__exit__ 2.__getattr__与__setattr__ -
元类的简介
1.type方法查看数据类型 2.type方法查看对象所属的类 3.type其实也是一个类 4.type是一个元类>>>:产生类的类 -
创建类的两种方式
1.关键字class 2.type元类 type(类名,父类,名称空间) -
元类定制类的产生行为
类中的 __init__ 是用来实例化对象的
元类中的 __init__ 就是用来实例化类的
class MyMetaClass(type):
def __init__(self,what,bases=None,dict={})
super().__init__(what,bases,dict) # 继承与调用父类里面的__init__方法,所以这个地方传的是实参,所以别传成what,bases=None,dict={}这样,这样就把要生成的类里面的父类与名称空间写死了。
class C1(metaclass=MyMetaClass):
pass
.
- 元类定制对象的产生行为
对象加括号执行产生该对象类中的 __call__
类加括号也应该执行产生该类的元类中的 __call__
class MyMetaClass(type):
def __call__():
1.产生空对象
2.调用类中的__init__实例化对象
3.返回该对象
class C1(metaclass=MyMetaClass):
pass
obj = C1()
.
.
-
双下new方法
产生空对象 -
设计模式及单例模式
1.设计模式简介 固定问题的固定模板 2.设计模式分类 23种 三大类(创建型、结构型、行为型) 3.单例模式 让类只能产生一个对象
今日内容概要
-
单例模式实现的多种方式
-
pickle序列化模块
-
选课系统需求分析
-
选课系统架构设计
-
选课系统目录搭建
-
选课系统功能搭建
-
选课系统管理员功能
今日内容详细
为什么要用单例?什么时候要用单例?
当类里面有很多功能,我们想用类里面的功能,但是类里面的功能都是绑定给对象的,所以得用类产生一个对象,再用对象才能调到类里面得很多方法,假设类里面写了很多牛逼得方法,都需要类的对象才能调,很多py文件都想用类里面的方法,这个时候用类的对象去调类里面的方法比较好,如果直接用类名去调的话,可能会出问题,因为功能里面可能有对象名点东西的句式,这个时候,用类名去调,传参就不好传了。都用对象去调就会产生很多个对象,就会比较占内存!!
单例模式实现的多种方式
第一种---------单例模式实现的方式:定义一个类方法,实现单例模式
class C1:
__instance = None
def __init__(self, name1, age1):
self.name = name1
self.age = age1
@classmethod # 绑定给类的方法语法糖
def singleton(cls): # singleton单例的意思
if not cls.__instance: # 判断上面的instance有没有值
cls.__instance = cls('jason', 18) # 如果没有值,改instance对应的值为一个实例化的对象
return cls.__instance # 如果__instance有值,直接返回对应的实例化对象
obj1 = C1.singleton()
obj2 = C1.singleton()
obj3 = C1.singleton()
print(id(obj1), id(obj2), id(obj3)) # 都是一个对象,详解见下图
这种有一个好处就是不是写死的,将来想产生一个全新的对象,就不掉绑定给对象的方法,就直接用类名加括号,触发类里面的__init__方法,这样就又可以产生新的对象了,这种方式比较灵活。
--------------------------------
obj4 = C1('kevin', 28) # 生成了一个新的对象
obj5 = C1('tony', 38) # 生成了一个新的对象
print(id(obj4), id(obj5))

.

.
object其实是一个类的实例,而这个类的名字是Object(默认类的命名首字母大写),它是所有类的父类,换句话说,python是默认所有的类都继承自Object类。而如abc(抽象基类)等其他的内置的类都是基于Object类的一些功能实现的。可以说,Object类规定了类的结构,加载方式,常用函数等。
.
第二种---------单例模式实现的方式:定制元类实现单例模式
class Mymeta(type): # 定义了一个元类,并继承了type元类
def __init__(cls, name, bases, dic): # 定义类Mysql时就触发
# 给类名称空间添加一个新的名字,并让它的值为一个空对象!!!object.__new__()产生空对象
cls.__instance = object.__new__(cls)
cls.__init__(cls.__instance, 'jason', 18) # 给空对象实例化
# 上面两边操作相当于在下面Mysql的类创建之后,立刻往类名称空间里面塞了一个__instance隐藏的名字,相当于就是对象名,就是我们以前用__init__方法括号里面的self,然后该名字对应的是一个对象!!!!!!
# 上述两步可以合成下面一步
# self.__instance=super().__call__(*args,**kwargs)
super().__init__(name, bases, dic)
def __call__(cls, *args, **kwargs): # Mysql()时触发
if args or kwargs: # args或kwargs内有值
obj = object.__new__(cls) # 还是先产生一个空对象
cls.__init__(obj, *args, **kwargs) # 给空对象实例化
return obj # 然后把对象名返出去,这样就实现了只要你往类名的括号里面传值,就会产生新对象
return cls.__instance # 这样就实现了,不忘类名的括号里面传值,一直返回的都是第一次走__init__产生的那个对象
class Mysql(metaclass=Mymeta): # 指定产生该类的元类为Mymeta!!!
def __init__(self, name, age):
self.name = name
self.age = age
print(Mysql.__dict__) # 见下图
----------------------
obj1 = Mysql()
obj2 = Mysql()
print(id(obj1), id(obj2)) # 两个对象是一样的
-------------------------
obj3 = Mysql('tony', 321) # 如果需要产生一个新的对象,往里面传值,就走if下面的代码了
obj4 = Mysql('kevin', 222)
print(id(obj3), id(obj4))
这样后面类名加括号的时候,不往括号里面传参数,__call__函数体代码里面args和kwargs就都为None,就会不走if里面的代码了,直接返回__instance
-----------------------------------

.

.

.
第三种---------单例模式实现的方式:基于模块的单例模式
提前产生一个对象 之后导模块使用
class C1:
def __init__(self, name):
self.name = name
obj = C1('jason') # 产生一个对象
把上面这几行代码,当成一个模块文件,放到单独的文件里面去
以后其他py文件要用的时候,先导该模块文件,然后通过模块名点obj,就永远得到的是一个对象了。为什么?
因为在一个py文件里面导导上面的模块,导了一次以后,就不会再导了,也就是上面的模块里面的代码执行完一次以后就不会再执行了,所以对于一个py文件而言,导入过一次上面的模块后,代码执行完一次以后就不会再执行了,所以在一个py文件里面,通过模块名点obj,拿到的都是一个对象了
----------------------------------------
第四种---------单例模式实现的方式:基于装饰器的单例模式
不看了,有空再看!!!
def outer(cls):
_instance = cls('jason', 18)
def inner(*args, **kwargs):
if args or kwargs:
obj = cls(*args, **kwargs)
return obj
return _instance
return inner
@outer # Mysql=outer(Mysql)
class Mysql:
def __init__(self, host, port):
self.host = host
self.port = port
obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3) # True
obj4 = Mysql('1.1.1.3', 3307)
obj5 = Mysql('1.1.1.4', 3308)
print(obj3 is obj4) # False
-----------------------------------
。
。
。
pickle序列化模块
优势:能够序列化python中所有的类型,就是什么都能序列化!!!主要用来不能被json序列化的数据。
缺陷: 序列化的结果,只能用python里面的pickle去识别,不能够兼容不同的语言,只能够在python中使用 无法跨语言传输。缺点很致命,注定了应用范围很小。
-------------------------------
需求:产生一个对象并保存到文件中,取出来还是一个对象
-------------------------------
class C1:
def __init__(self, name1, age1):
self.name = name1
self.age = age1
def func1(self):
print('from func1')
def func2(self):
print('from func2')
obj = C1('jason', 18)
实现步骤:
先试试直接写
class C1:
def __init__(self, name1, age1):
self.name = name1
self.age = age1
def func1(self):
print('from func1')
def func2(self):
print('from func2')
obj = C1('jason', 18)
---------------------------
with open(r'a.txt','w', encoding='utf8') as f:
f.write(obj)
# 写不进去,详见下图,write方法要求写入文件的必须是字符串,所以写不了。
---------------------------
利用json模块写
import json
with open(r'a.txt', 'w', encoding='utf8') as f:
json.dump(obj, f)
还是不行,对象不能被json序列化
---------------------------
利用pickle模块写
import pickle
with open(r'a.txt', 'wb') as f: # 注意在打开的时候是以二进制的模式打开的!!!
pickle.dump(obj, f) # 存,和json的使用方法一致,就模块名不一样
with open(r'a.txt', 'rb') as f:
data = pickle.load(f) # 取,和json的使用方法一致,就模块名不一样
print(data)
data.func1()
data.func2()
print(data.name)
这样就实现了,将对象写入文件,并从文件里面取出的操作了!!!
注意:pickle模块在使用的时候,当读出这个对象后,一定要确保当成产生对象的类体代码,和pickle读的代码在一个文件里面!!!因为如果不在一个文件,当对象读出来后,根本不知道对象是由哪个类产生的!!!就没办法真正还原成一个对象!!!
---------------------------

.

.

。
。
。
。
选课系统需求分析
选课系统
角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师5. 创建学员时,选择学校,关联班级
5. 创建讲师角色时要关联学校
6. 提供三个角色接口
6.1 学员视图, 可以注册, 交学费, 选择班级
6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里
功能提炼
1.管理员功能
注册功能
登录功能
创建学校
创建课程
创建老师
2.讲师功能
登录功能
选择课程
查看课程
查看学生分数
修改学生分数
3.学生功能
注册功能
登录功能
选择学校
选择课程
查看课程分数
选课系统架构设计
三层架构
与ATM架构设计的差异:
1.第一层做分层展示
2.第三层创建models.py存储所有的类 只有该py文件内的代码有资格调用db_handler
必须要通过模型层先拿到一个具体的对象,由对象去调db_handler里面封装的方法!!!

。
。
选课系统目录搭建
基于软件开发目录规范即可
选课系统功能搭建
先把整体的架子搭起来。
空函数,循环,功能字典
-------------------------------
# 先写start
import os
import sys
from core import src
base_dir = os.path.dirname(__file__)
sys.path.append(base_dir)
if __name__ == '__main__':
src.run()
-------------------------------
# 再写src
from core import admin_view, student_view, teacher_view
# 模块字典
module_dict = {
'1': admin_view,
'2': teacher_view,
'3': student_view
}
def run():
while True:
print("""
==============选课系统总视图==============
1.管理员视图
2.讲师视图
3.学生视图
========================================
""")
choice_num = input('请选择视图编号>>>:').strip()
if choice_num in module_dict:
module_dict.get(choice_num).run()
else:
print('暂无该视图编号')
--------------------------------------------
# 再写admin_view视图代码, teacher_view视图,student_view视图代码也同理操作一下
def register():
print('管理员注册功能')
def login():
print('管理员登录功能')
def create_school():
print('管理员创建学校功能')
def create_course():
print('管理员创建课程功能')
def create_teacher():
print('管理员创建老师功能')
func_dict = {
'1': register,
'2': login,
'3': create_school,
'4': create_course,
'5': create_teacher
}
def run():
while True:
print("""
++++++++++++++++管理员视图++++++++++++++++++
1.管理员注册功能
2.管理员登录功能
3.管理员创建学校
4.管理员创建课程
5.管理员创建讲师
+++++++++++++++++++++++++++++++++++++++++++
""")
choice_num = input('请选择管理员视图下的功能编号>>>:').strip()
if choice_num == 'q':
return
if choice_num in func_dict:
func_dict.get(choice_num)()
else:
print('暂无该管理员功能编号')
-----------------------------------
# 再开始写管理员里面分视图的里面各个功能的具体代码,先写注册功能
def register():
username = input('请输入您的用户名>>:').strip() # 1.获取管理员相关数据
password = input('请输入您的密码>>>:').strip()
ag_password = input('请确认您的密码>>>:').strip()
# 2.先判断两次密码是否一致
if not password == ag_password:
print('两次密码不一致')
else:
# 3.调用管理员注册接口
flag, msg = admin_interface.register_interface(username, password)
print(msg)
--------------------------------------
# 再开始写admin_interface里面的register_interface函数
def register_interface(username, password):
# 判断管理员用户名是否已存在
# 密码加密
# 创建管理员对象并保存
-----------------------------------
# 再开始写
# 创建一个管理员的类,专门将来帮我们产生管理员对象,这个类里面也记录了作为管理员可以拥有的所有的功能!!!
# 只要涉及到数据的增删改查的操作,都必须再db_handler里面进行操作,其他地方只能通过调db_handler里面的函数来操作!!
from db import db_handler
class Admin:
def __init__(self, name1, pwd1): # 类产生的对象需要有哪些独特的数据,在__init__里面要先写好!!!
self.name = name1
self.pwd = pwd1
self.save_obj() # 优化:当给对象添加独有数据后,自动调保存功能,这样就不需要在外面用对象点的方式来运行save_obj方法了!!!
def save_obj(self): # 先封装一个保存对象的方法!!!
db_handler.save(self) # 调db_handler里面专门用来保存数据的方法!!这个地方self是个对象
@classmethod # 因为下面的方法是给登录接口层里,通过类名点用的方式调用的,所以下面的方法绑定为类的方法
def select_obj(cls, username): # 类里面的查询用户名是否在db目录下对应的分目录下是否已存在功能函数!!
res = db_handler.select(cls, username) # 再调db_handler里面的查询方法,这个地方传cls类名的作用是:为了后续的路径拼接!!!
return res
------------------------------------
# 再开始到db_handler里面写db_handler.save()方法,这个方法以后既要保存学生对象,也要保存老师对象,还要保存管理员对象!!!
------
# 考虑到save()方法的对象要往db的目录下存,但是这个系统里面既有管理员,又有学生,又有老师,如果生成的对象都存放在db目录下就很乱,所以在db目录下再开管理员,学生和老师的目录,这样以后产生的对象,都放到对于的目录文件下,就比较好了!!!通过代码去创建这3个文件
------
需求:models文件里面有几个类,将来db里面就要有几个对应的目录
from conf import settings
import os
import pickle
def save(user_obj): # 对象保存功能!!!
# 因为保存的对象可能是学生,老师或者管理员,保存的文件都放在db目录下就很乱,最好就是models里面有几个类,
# 对应db目录下就有几个类名对应的文件夹专门用来存放类产生的对象!!!!!!怎么样通过代码实现动态创建了?
# 1.先考虑拼接管理员目录路径 db/admin
# 注意类名admin不是字符串形式的,但是我们拼接路径的时候需要的是字符串形式的admin
# 怎么拿到某个对象所属的类名(字符串形式的)???
# 当有类名的时候,通过类名.__name__ 就能够获取到类名对应的字符串名!!!
# 当我手上是某个类产生的对象,我们也想拿到对象所属的类的字符串名怎么办???
# obj.__class__.__name__
# 先通过obj.__class__拿对象所属的类的类名,再通过.__name__拿类名对应的字符串名!!!
# 这样终于可以根据传进来的对象,拿到所属的类的字符串名了!!!操!!!拼接出db目录下类对应的目录路径
admin_dir_name = os.path.join(settings.DB_DIR, user_obj.__class__.__name__)
# 2.判断目录是否存在,并动态创建!!!
if not os.path.exists(admin_dir_name):
os.mkdir(admin_dir_name)
# 3.拼接存储管理员对象数据的文件路径,user_obj.name对象点的方式拿name对应的值
admin_file_path = os.path.join(admin_dir_name, user_obj.name)
# 直接拿对象的用户名作为保存文件的文件名不需要什么后缀了!!!
with open(admin_file_path, 'wb')as f:
pickle.dump(user_obj, f)
# 暂时大功告成了!!!这样就可以根据传进来的对象,拿到产生对象的类名(字符串形式),作为创建db里面分目录的目录名,
# 这样就完成了对象的保存功能了
----------------------------------
def select(class_name, username):
admin_dir_name = os.path.join(settings.DB_DIR, class_name.__name__) # 1. 通过类名拿类名的字符串形式,并拼出分目录路径!!
# 2.判断目录是否存在,并动态创建!!!
if not os.path.exists(admin_dir_name):
os.mkdir(admin_dir_name)
# 3.拼接存储管理员对象数据的文件路径
admin_file_path = os.path.join(admin_dir_name,username) # 这个地方username已经是字符串形式的了
if os.path.exists(admin_file_path): # 和购物车套路一样,路径存在,直接将对应的文件返回出去!!!
with open(admin_file_path, 'rb')as f:
return pickle.load(f)
这样db_handler里面差不多就写好了
---------------------------
# admin_view里面代码
from interface import admin_interface
def register():
username = input('请输入您的用户名>>:').strip() # 1.获取管理员相关数据
password = input('请输入您的密码>>>:').strip()
ag_password = input('请确认您的密码>>>:').strip()
# 2.先判断两次密码是否一致
if not password == ag_password:
print('两次密码不一致')
else:
# 3.调用管理员注册接口
flag, msg = admin_interface.register_interface(username, password) # 管理员简易版本的注册功能写好了
print(msg) # 这样注册功能就差不多写好了
--------------
def login():
# 1.获取管理员用户名与密码
username = input('请输入您的用户名>>:').strip()
password = input('请输入您的密码>>>:').strip()
# 2.调用第二次登录接口
flag, msg = admin_interface.login_interface(username, password)
print(msg) # 这样登录功能就差不多写好了
-------------------------------------------
# admin_interface 里面代码
# 注意第二层只能和models里面的类打交道,不能和db_handler打交道!!!
from db import models
from lib import common
def register_interface(username, password):
# 1.判断管理员用户名是否已存在(判断在db目录里面的管理员目录里面用户名是否已存在!!!)
# 这个地方只能调models里面的admin类,所以在管理员的类里面应该有一个方法,用来帮我们查当前用户名在管理员目录下是否已存在!!!
user_obj = models.Admin.select_obj(username) # 调用管理员类里面的查询功能!!!
if user_obj:
return False, f'当前管理员用户名{username}已存在,请重新注册'
# 2.密码加密
hash_pwd = common.get_hash(password)
# 3.创建管理员对象并保存
admin_obj = models.Admin(username, hash_pwd) # 类名加括号生成对象!!
# admin_obj.save_obj() # 对象通过点调用类里面的方法并运行,保存对象!!,类里面__init__里面有了调用save_obj()方法了,所以该代码不用写了
return True, f'管理员{username}注册成功!!!'
-----------------------------
def login_interface(username, password):
# 1.校验当前用户名是否存在!!!
user_obj = models.Admin.select_obj(username) # 调用管理员类里面的查询功能!!!
if not user_obj:
return False, f'当前管理员用户名{username}不存在,请重新登录!!'
# 2.判断密码是否一致!!
if not user_obj.pwd == common.get_hash(password):
return False, '密码错误'
return True, f'管理员{username}登录成功!!'
----------------------------------------
# common里面的代码
import hashlib
def get_hash(msg):
md5 = hashlib.md5()
md5.update(msg.encode('utf8'))
return md5.hexdigest()
这样差不多管理员的注册登录功能就写好了!!!!!!
.
.
.
.
.
.
.
.
重来一下
settings 代码:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DIR, 'db')
if not os.path.exists(DB_DIR):
os.mkdir(DB_DIR)
-----------------------------------------
admin_view 里面代码
from interface import admin_interface
from lib import common
is_login = {'username': ''}
def register():
username = input('请输入用户名>>>>:').strip()
password = input('请输入密码>>>>:').strip()
ag_pwd = input('请再次输入密码>>>>:').strip()
if not password == ag_pwd:
print('两次密码不一致')
return
flag, msg = admin_interface.register_interface(username, password)
print(msg)
def login():
username = input('请输入用户名>>>>:').strip()
password = input('请输入密码>>>>:').strip()
flag, msg = admin_interface.login_interface(username, password)
if flag:
is_login['username'] = username
print(msg)
@common.login_auth('admin')
def creat_school():
admin_name = is_login['username']
school_name = input('请输入要创建的学校名称>>>:').strip()
school_address = input('请输入该学校的地址>>>:').strip()
flag, msg = admin_interface.creat_school_interface(school_name, school_address, admin_name)
print(msg)
@common.login_auth('admin')
def creat_course():
admin_name = is_login['username']
# 获取学校列表,选择学校后,再创建课程
flag, school_list1 = admin_interface.get_school_list_interface()
if not flag:
print(school_list1) # '暂无学校,请先创建学校!!!'
return
while True:
for num, school_name in enumerate(school_list1, start=1):
print(f"学校编号:{num} | 学校名称:{school_name}")
school_num = input('请输入要选择的学校编号(q)>>>:').strip()
if school_num == 'q':
return
if not school_num.isdigit():
print('请输入纯数字!!')
continue
school_num = int(school_num)
if school_num not in range(1, len(school_list1) + 1):
print('该学校编号不在对应的范围内')
continue
target_school_name = school_list1[school_num - 1] # 拿到选择的学校名称
course_name = input('请输入要创建的课程名称>>>:').strip() # 名称_期数_学校 好区分!!
course_price = input('请输入该课程的价格>>>:').strip()
course_period = input('请输入该课程的周期>>>:').strip()
# 调接口
flag, msg = admin_interface.creat_course_interface(target_school_name,
course_name,
course_price,
course_period,
admin_name
)
print(msg)
@common.login_auth('admin')
def creat_teacher():
admin_name = is_login['username']
teacher_name = input('请输入要创建的老师名称>>>:').strip()
flag, msg = admin_interface.creat_teacher_interface(teacher_name, admin_name)
print(msg)
func_dict = {
'1': register,
'2': login,
'3': creat_school,
'4': creat_course,
'5': creat_teacher
}
def run():
while True:
print("""
-------------管理员视图----------------
1.注册功能
2.登录功能
3.创建学校功能
4.创建课程功能
5.创建老师功能
----------------end------------------
""")
choice_func_num = input('请输入要执行的功能编号(q)>>>:').strip()
if choice_func_num == 'q':
return
if choice_func_num in func_dict:
func_dict.get(choice_func_num)()
else:
print('暂无您要执行的编号>>>:')
----------------------------
作业
1.整理今日内容及博客
2.尝试编写校验用户是否登录装饰器并思考如何区分用户角色 不做出误判
3.尝试编写管理员其他功能

浙公网安备 33010602011771号