day03-学生管理系统-面向对象
魔术方法: __ dict __将对象的属性和属性值封装为字典
用字典的值实例化对象: 对象名(**字典) => 拆包
student.py
""" 该文件记录的是: 学生类的信息. 学生的属性如下: 姓名, 性别, 年龄, 联系方式, 描述信息 """ # 1. 定义学生类. class Student(object): # 2. 初始化学生信息 def __init__(self, name, gender, age, mobile, des): """ 该魔法方法用于 初始化 学生的属性信息. :param name: 姓名 :param gender: 性别 :param age: 年龄 :param mobile: 手机号 :param des: 描述信息 """ self.name = name self.gender = gender self.age = age self.mobile = mobile self.des = des # 3. 打印学生信息. def __str__(self): # print(__name__) return f'姓名: {self.name}, 性别: {self.gender}, 年龄: {self.age}, 手机号: {self.mobile}, 描述信息: {self.des}' # return '姓名: %s, 性别: %s, 年龄: %d, 手机号: %s, 描述信息: %s' % (self.name, self.gender, self.age, self.mobile, self.des) # 4. 记得在main函数中测试, 否则别人导入这个模块的时候, 会自动执行如下的测试代码. if __name__ == '__main__': # 5. 创建学生对象. s = Student('乔峰', '男', 39, '13112345678', '丐帮帮主') # 6. 打印学生信息. print(s)
student_cms.py
""" 该文件记录的是: 学生管理系统类的信息. 名词 cms 解释: 全称叫: Content Management System, 内容管理系统. 学生管理系统类 StudentCms 属性: stu_info = [{学生信息}, {学生信息}...] 即: stu_info = [学生对象, 学生对象...] 行为: 函数 __init__() # 初始化属性信息 show_view() # 打印提示信息, 无需使用self对象, 所以设置为: 静态方法. add_student() # 添加学生信息 del_student() # 删除学生信息 update_student() # 修改学生信息 search_one_student() # 查询单个学生信息 search_all_student() # 查询所有学生信息 save_student() # 保存学生信息 => 文件中, 存档. load_student() # 从文件中 => 学生信息, 读档. start() # 表示整体的业务逻辑 => 框架 """ # 导包 import time import os # Operating System: 系统模块. from student import Student # 1. 定义学生管理系统类. class StudentCms(object): # 2. 定义初始化属性. def __init__(self): # 格式: [{学生信息}, {学生信息}...], 即: [学生对象, 学生对象...] self.stu_info = [] # 为了提高效率, 准备的测试数据 # s1 = Student('乔峰', '男', 39, '131', '丐帮帮主') # s2 = Student('阿朱', '女', 35, '151', '丐帮帮主夫人') # s3 = Student('虚竹', '男', 31, '161', '灵鹫宫宫主') # s4 = Student('李清露', '女', 25, '171', '灵鹫宫宫主夫人') # self.stu_info = [s1, s2, s3, s4] # 3. show_view() # 打印提示信息, 设置为: 静态方法. @staticmethod def show_view(): print("*" * 21) print("欢迎使用学生管理系统V2.0") print("\t1. 添加学生信息") print("\t2. 修改学生信息") print("\t3. 删除学生信息") print("\t4. 查询单个学生信息") print("\t5. 查询所有学生信息") print("\t6. 保存学生信息") print("\t7. 退出系统") print("*" * 21) # 4. add_student() # 添加学生信息 def add_student(self): # 4.1 提示用户录入学生信息 并接收. name = input('请录入学生的姓名: ') gender = input('请录入学生的性别: ') age = input('请录入学生的年龄: ') mobile = input('请录入学生的手机号: ') des = input('请录入学生的描述信息: ') # 4.2 将上述的信息封装成 学生对象. new_stu = Student(name, gender, age, mobile, des) # 4.3 将学生对象添加到列表中. self.stu_info.append(new_stu) # 4.4 打印提示信息. print(f"添加学生 {name} 的信息成功!\n") # 5. del_student() # 删除学生信息 def del_student(self): # 5.1 提示用户录入要删除的学生 姓名. 扩展: 假设存在重名学生, 都要删掉, 自己代码实现. del_name = input('请录入要删除的学生姓名: ') # 5.2 遍历学生列表, 获取每个学生信息. for stu in self.stu_info: # stu: 就是具体的每个学生对象. # 5.3 判断当前学生信息, 是否是 要删除的学生. if stu.name == del_name: # 5.4 走这里, 删除该学生对象即可. self.stu_info.remove(stu) print(f"删除学生 {del_name} 的信息成功!\n") break else: # 5.5 走这里, 说明没有找到要删除的学生. print(f'未找到 {del_name} 学生信息, 请校验后重新操作!\n') # 6. update_student() # 修改学生信息 def update_student(self): # 6.1 提示用户录入要修改的学生 姓名, 并接收. update_name = input('请录入要修改的学生姓名: ') # 6.2 遍历学生列表, 获取每个学生的信息. for stu in self.stu_info: # stu: 就是具体的每个学生对象. # 6.3 判断当前学生信息, 是否是 要修改的学生. if stu.name == update_name: # 6.4 走这里, 提示用户, 录入要修改的信息, 并修改当前学生的信息. stu.gender = input('请录入新的性别: ') stu.age = input('请录入新的年龄: ') stu.mobile = input('请录入新的手机号: ') stu.des = input('请录入新的描述信息: ') # 6.5 提示, 并结束. print(f"修改 {update_name} 的信息成功!\n") break else: # 6.6 走这里, 说明没有找到要修改的学生. print(f'未找到 {update_name} 学生信息, 请重新操作!\n') # 7. search_one_student() # 查询单个学生信息 def search_one_student(self): # 7.1 提示用户录入要查询的学生 姓名, 并接收. search_name = input('请录入要查询的学生姓名: ') # 7.2 遍历学生列表, 获取每个学生的信息. for stu in self.stu_info: # stu: 就是具体的每个学生对象. # 7.3 判断当前学生信息, 是否是 要查询的学生. if stu.name == search_name: # 7.4 走这里, 说明找到了, 打印即可. # print(stu, '\n') print(stu, end='\n\n') break else: # 7.5 走这里, 说明没有找到要修改的学生. print(f'未找到 {search_name} 学生信息, 请重新操作!\n') # 8. search_all_student() # 查询所有学生信息 def search_all_student(self): # 8.1 判断列表中是否有 学生信息. if len(self.stu_info) <= 0: # 8.2 走这里, 说明暂无学生信息. print('暂无学生信息, 请添加后重新查询!\n') else: # 8.3 走这里, 说明有学生信息, 遍历打印即可. for stu in self.stu_info: print(stu) print() # 换行, 让格式更加好看. # 9. save_student() # 保存学生信息 => 文件中, 存档. def save_student(self): # 9.1 把 学生对象列表 => 列表嵌套字典的形式, 即: [学生对象, 学生对象...] => [{学生信息}, {学生信息}...] stu_dict_list = [stu.__dict__ for stu in self.stu_info] # 9.2 把上述的学生信息(列表嵌套字典), 写到目的地文件 student.data 文件中. with open('./student.data', 'w', encoding='utf-8') as dest_f: # dest: 目的地 # 9.3 具体的写的动作. # 先把 列表嵌套字典, 转换成 字符串, 再写入到目的地文件中. dest_f.write(str(stu_dict_list)) # 9.4 提示即可. print('学生信息存档成功!\n') # 10. load_student() # 从文件中 => 学生信息, 读档. def load_student(self): # 10.1 判断数据源文件是否存在. if os.path.isfile('./student.data'): # 判断 student.data 必须是 存在的 文件才行. # 10.2 走到这里, 说明源文件存在, 读取文件信息即可. with open('./student.data', 'r', encoding='utf-8') as src_f: # 10.3 具体的读取文件数据的操作. # 格式为: "[{'name': '乔峰', 'gender': '男', 'age': 39, 'mobile': '131', 'des': '丐帮帮主'}, {学生信息}, {学生信息}...]" str_list_data = src_f.read() # 10.4 判断 字符串的长度是否合法, 如果不合法, 给个默认值. if len(str_list_data) <= 0: str_list_data = '[]' # 10.5 把上述的 字符串形式的 列表嵌套字典 => 列表嵌套字典 的格式. # 格式为: [{'name': '乔峰', 'gender': '男', 'age': 39, 'mobile': '131', 'des': '丐帮帮主'}, {学生信息}, {学生信息}...] list_data = eval(str_list_data) # eval(): 去掉最外边的那组引号, 剩下的是啥就转成什么类型. # 10.6 把上述的列表嵌套字典形式的 学生数据 => 学生列表对象, 即: [学生对象, 学生对象...] self.stu_info = [Student(**stu_dict) for stu_dict in list_data] else: # 10.7 走这里, 说明文件不存在, 创建即可. # src_f = open('./student.data', 'w', encoding='utf-8') # src_f.close() # 扩展: 你去查一下 os模块中是否有直接创建文件的函数, 有可以直接使用它来完成需求. os.open('./student.data', flags=os.O_CREAT) # 效果同上. # 11. start() # 表示整体的业务逻辑 => 框架 def start(self): # 11.0 加载学生的信息, 类似于: 读档. self.load_student() # 11.1 因为是重复操作的, 所以用 while(True) 死循环. while True: # 11.2 模拟用户等待 time.sleep(1) # 1秒 # 11.3 打印提示信息 # self.show_view() # 静态方法, 调用方式1: 对象名. 可以, 但是不推荐. StudentCms.show_view() # 静态方法, 调用方式1: 类名. 推荐 # 11.4 提示用户录入他/她要操作的编号, 并接收. num = input('请录入您要操作的编号: ') # 11.5 基于用户录入的编号, 判断, 并进行对应的操作. if num == '1': # print("1. 添加学生信息") self.add_student() elif num == '2': # print("2. 修改学生信息") self.update_student() elif num == '3': # print("3. 删除学生信息") self.del_student() elif num == '4': # print("4. 查询单个学生信息") self.search_one_student() elif num == '5': # print("5. 查询所有学生信息") self.search_all_student() elif num == '6': # print("6. 保存学生信息") self.save_student() elif num == '7': # 细节: 退出系统, 给一个提示信息. result = input('您确定要退出吗, y(退出), 其它(继续) => ') if result.lower() == 'y': # lower()函数: 把字母转成其对应的 小写形式. time.sleep(2) # 细节: 也可以考虑, 在退出之前(就是这里), 再次保存下学生信息. # self.save_student() print('感谢您的使用, 再见!') break else: print('录入有误, 请重新录入!\n') # 12. 在main方法中测试. if __name__ == '__main__': stu_cms = StudentCms() stu_cms.start()
main.py
""" 该文件记录的是: 学生管理系统的主入口 目前的文件 student.py => 存放 Student 学生类信息的 student_cms.py => 存放 StudentCms 学生管理系统类 信息的. """ # 导包 from student_cms import StudentCms # 1. 制定程序的主入口 if __name__ == '__main__': # 2. 创建学生管理系统类 对象 stu_cms = StudentCms() # 3. 启动程序. stu_cms.start()
深浅拷贝
回顾可变与不可变类型
划分依据
在不改变地址值的情况下, 是否可以修改内容, 可以 => 可变类型, 不可以 => 不可变类型.
代码
# 需求1: 演示不可变类型. a = 10 print(f'a的值: {a}') # 10 print(f'a的地址: {id(a)}') # 0x01 # 修改不可变类型 a = 20 print(f'a的值: {a}') # 20 print(f'a的地址: {id(a)}') # 0x02 print('-' * 21) # 需求2: 演示可变类型. list1 = [1, 2, 3] print(f'list1的值: {list1}') # [1, 2, 3] print(f'list1的地址: {id(list1)}') # 0x03 # 修改可变类型 list1[1] = 200 print(f'list1的值: {list1}') # [1, 200, 3] print(f'list1的地址: {id(list1)}') # 0x03
深浅拷贝介绍
-
所谓的深浅拷贝, 指的是: 拷贝的多与少. 深拷贝拷贝的多, 浅拷贝拷贝的少.
-
深浅拷贝都可以操作可变 和 不可变类型, 但是深浅拷贝一般不会操作不可变类型, 且你遇到的面试题几乎都是: 深浅拷贝操作可变类型.
-
回顾可变和不可变类型, 划分依据: 在不改变地址值的情况下, 是否可以修改内容, 可以 => 可变类型, 不可以 => 不可变类型.可变类型: 列表, 字典, 集合不可变类型: 字符串, 整数, 浮点型, 元组, 布尔...
-
所谓的深浅拷贝, 指的就是 copy 模块的不同函数.浅拷贝: copy.copy()深拷贝: copy.deepcopy()
代码演示
# 导包 import copy # python的赋值操作属于引用赋值(eg:b是a的别名, 形参是实参的别名) def dm01_普通赋值(): # 1 python中的赋值操作, 属于引用赋值 (把a的地址赋值给b) # 2 b是a的别名, b和a都指向相同的内存空间 a = 10 b = a print('id(a)-->', id(a)) # 0x01 print('id(b)-->', id(b)) # 0x01 print('id(10)-->', id(10)) # 0x01 # 3 也是引用赋值 c和d指向相同的内存空间 a = [1, 2, 3] b = [11, 22, 33] c = [a, b] d = c print('id(c)-->', id(c)) # 0x03 print('id(d)-->', id(d)) # 0x03 # 4 值的方式赋值 a 指向一块内存空间、b 也指向一块内存空间 # b = a python中不支持, 这样做传参效率高 # 浅拷贝可变类型: 只拷贝第1层数据, 深层次数据不拷贝 def dm02_浅拷贝可变类型(): a = [1, 2, 3] b = [11, 22, 33] c = [6, 7, a, b] # 测试1 id(c)和id(d) d = copy.copy(c) # 浅拷贝 = 只会拷贝 可变类型的 第1层数据. print('id(c)-->', id(c)) # 0x03 print('id(d)-->', id(d)) # 0x04 print("id(c)和id(d)值不一样, 说明浅拷贝第1层(最外面一层的数据)") # 测试2 print(id(c[2])) # 0x01 print(id(a)) # 0x01 print("id(c[2])和id(a)值一样, 说明浅拷贝第2层的数据") # 修改a[2] = 22 a[2] = 22 # c[0] = 100 print('c->', c) # [6, 7, [1, 2, 22], [11, 22, 33]] print('d->', d) # [6, 7, [1, 2, 22], [11, 22, 33]] # 浅拷贝不可变类型: 不会给拷贝的对象c开辟新的内存空间, 而只是拷贝了这个对象的引用 def dm03_浅拷贝不可变类型(): # 不可变类型 a b c a = (1, 2, 3) b = (11, 22, 33) c = (6, 7, a, b) d = copy.copy(c) print('id(c)-->', id(c)) # 0x03 print('id(d)-->', id(d)) # 0x03 print("id(c)和id(d)值一样, 说明c和d指向相同的内存空间") # 深拷贝可变类型: 若为可变类型开辟新的内存空间,所有层都会深拷贝 # 作用: 能保证数据的安全 def dm04_深拷贝可变类型(): a = [1, 2, 3] b = [11, 22, 33] c = [6, 7, a, b] d = copy.deepcopy(c) print('id(c)-->', id(c)) # 0x03 print('id(d)-->', id(d)) # 0x04 a[1] = 100 b[1] = 800 print(f'c: {c}') # [6, 7, [1, 100, 3], [11, 800, 33]] print(f'd: {d}') # [6, 7, [1, 2, 3], [11, 22, 33]] # 深拷贝不可变类型: 若为不可变类型直接就引用了, 不开辟新的内存空间 def dm05_深拷贝不可变类型(): a = (1, 2, 3) b = (11, 22, 33) c = (a, b) d = copy.deepcopy(c) print(id(c)) # 0x03 print(id(d)) # 0x03 print("c/d内存空间相同, 说明c和d指向相同的内存空间") # 在main函数中测试 if __name__ == '__main__': # dm01_普通赋值() # dm02_浅拷贝可变类型() # dm03_浅拷贝不可变类型() # dm04_深拷贝可变类型() dm05_深拷贝不可变类型()
图解深浅拷贝
普通赋值内存图
浅拷贝操作可变类型
浅拷贝操作不可变类型
深拷贝操作可变类型
深拷贝操作不可变类型
等同于普通赋值操作和浅拷贝操作不可变类型
总结
list1 = [1, 2, list2]
深浅拷贝操作不可变类型时相当于普通赋值操作, 即增加地址指向, 并不会重新开辟内存地址
浅拷贝操作可变类型: 拷贝第一层, 拷贝内容为:list0 = [1, 2, list2的地址指向]
修改list2的内容对拷贝后的数据直接影响, 修改list1无影响
深拷贝操作可变类型: 拷贝所有可变类型的层级, 即: 将list1拷贝, 并将list1中的list2同时拷贝一份, 修改元数据对拷贝后的数据无影响, 重新开辟内存空间