Python进阶(六)案例:面向对象版学员管理系统
目标
- 了解面向对象开发过程中类内部功能的分析方法
- 了解常用系统功能
- 添加
- 删除
- 修改
- 查询
重点学习部分
重点学习:
-
类与对象、列表增删:[3.2.2 管理系统框架](# 3.2.2 管理系统框架 )、[3.4.1 添加功能](# 3.4.1 添加功能 )
-
文件操作、类与列表字典转换:[3.4.7 保存学员信息](# 3.4.7 保存学员信息 ) 、[3.4.8 加载学员信息](# 3.4.8 加载学员信息)
-
列表增删及推导式: [3.4.2 删除学员](#3.4.2 删除学员 )
一. 系统需求
使用面向对象编程思想完成学员管理系统的开发,具体如下:
-
系统要求:学员数据存储在文件中
-
系统功能:添加学员、删除学员、修改学员信息、查询学员信息、显示所有学员信息、保存学员信息及退出系统等功能。
二. 准备程序文件
2.1 分析
-
角色分析
- 学员
- 管理系统
工作中注意事项
- 为了方便维护代码,一般一个角色一个程序文件;
- 项目要有主程序入口,习惯为
main.py
2.2 创建程序文件
创建项目目录,例如:StudentManagerSystem
程序文件如下:
-
程序入口文件:main.py
-
学员文件:student.py
-
管理系统文件:managerSystem.py
三. 书写程序
3.1 student.py
需求:
-
学员信息包含:姓名、性别、手机号;
-
添加
__str__
魔法方法,方便查看学员对象信息
3.1.2 程序代码
class Student(object): |
3.2 managerSystem.py
需求:
-
存储数据的位置:文件(student.data)
- 加载文件数据
- 修改数据后保存到文件
-
存储数据的形式:列表存储学员对象
-
系统功能
- 添加学员
- 删除学员
- 修改学员
- 查询学员信息
- 显示所有学员信息
- 保存学员信息
- 退出系统
3.2.1 定义类
class StudentManager(object): |
3.2.2 管理系统框架
需求:系统功能循环使用,用户输入不同的功能序号执行不同的功能。
-
步骤
- 定义程序入口函数
- 加载数据(读取文件中的数据到系统)
- 显示功能菜单
- 用户输入功能序号
- 根据用户输入的功能序号执行不同的功能
- 定义系统功能函数,添加、删除学员等
- 定义程序入口函数
class StudentManager(object): |
**问题1:**这段代码里为啥可以这样调用函数用self.函数名()
?
在Python中,
self
是指类实例对象本身。在类的方法中,第一个参数通常是self
,它表示调用该方法的实例对象。当你使用self.函数名()
时,你实际上是在调用该实例对象的方法。在你给出的代码中,
ManagerSystem
类定义了许多方法,如add_student
,del_student
,modify_student
等。当你创建一个ManagerSystem
类的实例并调用它的run
方法时,该方法会根据用户输入的功能序号来调用相应的方法。例如,如果用户输入1,则会调用self.add_student()
方法来添加学员。因此,在这段代码中,使用
self.函数名()
是为了在类的一个方法中调用该类的另一个方法。这样可以让代码更加模块化和可重用。
**问题2:**调用不能直接写成函数名嘛?为什么调用的函数可以写在被调用函数的上面?
在类的方法中,不能直接使用函数名来调用该类的另一个方法。这是因为在类的方法中,你需要明确指定调用方法的实例对象。
self
就是用来表示调用该方法的实例对象。因此,你需要使用self.函数名()
来调用该实例对象的另一个方法。至于为什么调用的函数可以写在被调用函数的上面,这是因为在Python中,类中定义的方法并不会立即执行。当你创建一个类的实例并调用它的某个方法时,Python解释器会在类定义中查找该方法并执行它。因此,只要在调用方法之前定义了该方法,就可以正常调用。
例如,在你给出的代码中,
run
方法中调用了show_menu
方法。尽管show_menu
方法定义在run
方法之后,但当你创建一个ManagerSystem
类的实例并调用它的run
方法时,Python解释器仍然能够在类定义中找到show_menu
方法并执行它。
3.3 main.py—程序入口文件
# 1. 导入managerSystem模块 |
3.4 定义系统功能函数
3.4.1 添加功能
-
需求:用户输入学员姓名、性别、手机号,将学员添加到系统。
-
步骤
- 用户输入姓名、性别、手机号
- 创建该学员对象
- 将该学员对象添加到列表(并未保存,保存有单独功能)
-
代码
# 添加学员函数内部需要创建学员对象,故先导入student模块 |
问题1:代码中的student输入到列表student_list中元素是啥数据类型?
在这段代码中,
student
是一个学员对象,它是通过使用Student
类创建的,故数据类型是 学员对象,学员对象自带Student类的属性和方法。当我们调用
Student(name, gender, tel)
时,会创建一个新的学员对象,并将用户输入的姓名、性别和电话作为参数传递给Student
类的构造函数。
Student(name, gender, tel)
这个表达式返回一个学员对象,并将其赋值给变量student
。这个学员对象具有名为name
、gender
和tel
的属性,这些属性的值是根据用户输入的数据来设定的。接下来,我们将这个学员对象
student
添加到self.student_list
列表中,使得列表的每个元素都代表一个学员对象。所以,
self.student_list
列表中的元素是学员对象。列表的长度就等于学员对象的数量,每个学员对象都包含姓名、性别和电话等信息。
3.4.2 删除学员
-
需求:用户输入目标学员姓名,如果学员存在则删除该学员。
-
步骤
- 用户输入目标学员姓名
- 遍历学员数据列表,如果用户输入的学员姓名存在则删除,否则则提示该学员不存在。
-
代码
# 2.3 删除学员:删除指定姓名的学员 |
**问题1:**为啥i.name
可以找到学员姓名,i不是self.student_list
下标嘛?(见3.4.1添加功能‘问题1‘能弄清楚i是啥)
**解释一:**在提供的代码中,
self.student_list
是一个列表,用于存储学员对象student。列表中的每个元素(即循环中的 i )都代表一个学员对象student。学员对象是通过使用Student
类创建的。当我们使用
for i in self.student_list
循环遍历列表时,变量i
代表当前正在迭代的学员对象student,而不是列表的下标。在循环体内,我们可以通过
i.name
访问学员对象的name属性。这是因为学员对象是使用Student
类创建的,该类具有名为name
的属性。所以,i.name
实际上是获取当前迭代的学员对象的name属性。在删除学员对象时,我们将输入的学员姓名 (
del_name
) 与self.student_list
中的每个 学员对象的name
属性进行比较,以找到需要删除的学员对象。**解释二:**在这段代码中,
i
是一个变量,它表示self.student_list
列表中的每个元素(也就是学员对象)。每次循环迭代时,i
会依次指向列表中的每个学员对象student。而
name
是Student
类的一个属性,每个学员对象都具有这个属性。因此,通过使用i.name
,我们可以获取当前迭代的学员对象的name属性。在删除学员操作中,我们将输入的学员姓名 (
del_name
) 与当前迭代的学员对象的姓名属性 (i.name
) 进行比较。如果两者匹配,说明找到了要删除的学员对象。然后,我们使用remove()
方法从列表中移除该学员对象,以实现删除操作。所以,在这段代码中,
i.name
表示当前迭代的学员对象的姓名属性,用于比较和操作学员对象。
3.4.3 修改学员信息
-
需求:用户输入目标学员姓名,如果学员存在则修改该学员信息。
-
步骤
- 用户输入目标学员姓名;
- 遍历学员数据列表,如果用户输入的学员姓名存在则修改学员的姓名、性别、手机号数据,否则则提示该学员不存在。
-
代码
# 2.4 修改学员信息 |
3.4.5 查询学员信息
-
需求:用户输入目标学员姓名,如果学员存在则打印该学员信息
-
步骤
- 用户输入目标学员姓名
- 遍历学员数据列表,如果用户输入的学员姓名存在则打印学员信息,否则提示该学员不存在。
-
代码
# 2.5 查询学员信息 |
3.4.6 显示所有学员信息
-
打印所有学员信息
-
步骤
- 遍历学员数据列表,打印所有学员信息
-
代码
# 2.6 显示所有学员信息 |
3.4.7 保存学员信息
-
需求:将修改后的学员数据保存到存储数据的文件。
-
步骤
- 打开文件
- 文件写入数据
- 关闭文件
思考
- 文件写入的数据是学员对象的内存地址吗?——不是,需要是列表里字典,而字典存储学员对象
- 文件内数据要求的数据类型是什么?——字符串
- 文件是如何存储数据:存储到列表,列表一个元素表示一个学员是用Student类建立的学员对象
-
拓展
__dict__
类对象或实例对象都拥有的属性,该属性的作用:收集类或实例对象的属性和方法以及值,从而返回一个字典
class A(object): |
在Python中
-
代码
# 2.7 保存学员信息 |
核心点:
**注意1:**文件写入的数据不能是学员对象的内存地址,需要把学员数据转换成列表字典数据再做存储,即:将[学员对象] 转换为 [{}]
new_list = [i.__dict__ for i in self.student_list] # [学员对象] 转换成 [字典]**注意2:**文件内数据要求为字符串类型,故需要先转换数据类型为字符串才能文件写入数据,即:将 [{}]类型 转换为 字符串类型
f.write(str(new_list)) # 将[{}]类型 转换为 字符串类型
3.4.8 加载学员信息
-
需求:每次进入系统后,修改的数据是文件里面的数据
-
步骤
- 尝试以
"r"
模式打开学员数据文件,如果文件不存在则以"w"
模式打开文件 - 如果文件存在则读取数据并存储数据
- 读取数据
- 转换数据类型为列表并转换列表内的字典为对象
- 存储学员数据到学员列表
- 关闭文件
- 尝试以
-
代码
# 2.8 加载学员信息 |
eval
作用:eval()
函数在这段代码中的作用是将从文件中读取的字符串数据解析为字典类型的列表数据,以便进一步转换为学员对象的列表。将字符串类型解析为含字典的列表类型
核心点:
读取数据:将文件读取出的数据是字符串类型解析为含字典的列表类型:字符串类型 转换为 [{}]类型
new_list = eval(data)
将[{}] 转换为 [学员对象]
self.student_list = [Student(i['name'], i['gender'], i['tel']) for i in new_list]
四. 总结
-
函数
- 定义和调用
- 参数的使用
-
面向对象
- 定义类
- 创建对象
- 定义和调用实例属性
- 定义和调用实例方法
-
数据类型
- 列表
- 增加删除数据
- 列表推导式
- 字典
- 字符串
- 列表
-
文件操作
- 打开文件
- 读取或写入
- 关闭文件