4.7
今日正课
面向对象编程
- 一种编程思想,与面向对象一样相当于武功不同的流派,没有高低之分,各自有应用场景
- 面向过程,核心是“过程”,过程是流水线,是用来解决问题的步骤,核心思想是将程序流程化
- 面向对象,核心是对象
对象
- 对象是“容器”,是用来盛数据和功能的,可以看作数据和功能的集合体
- 对象的奥义就是将程序“整合”
举例
需求1:选课系统
# 程序中应该有学生的数据,课程的信息,以及操作的功能
# 学生信息
stu_name = 'deimos'
stu_age = 18
stu_sex = 'male'
# 学生功能,例:打印学生的信息
def show_info():
print('名字:%s 年龄:%s 性别:%s'%(stu_name,stu_age,stu_sex))
# 学生功能,改学生信息
def set_info():
global stu_name,stu_sex,stu_age
stu_name =x
stu_sex = y
stu_age = z
# 课程数据
course_name = 'python'
course_period = '6mons'
course_score = 10
# 课程的功能,例:打印课程信息
def show_course():
print(course_name,course_period,course_score)
像这样,数据和功能分开,我们发现有一些功能只会操作一部分数据,如课程功能只操作课程的数据,但是所有的数据和功能都放在一起,混乱。所以希望把每种功能和数据分别进行整合
# 学生的容器 = 学生的数据 + 学生的功能
# 课程的容器 = 课程的数据 + 课程的功能
整合后,不同类型的功能和数据分割开,解开耦合
需求2:化妆
-
要化妆,需要准备好原材料和工具
- 原材料:眼影,粉底
- 工具:眼线笔,粉扑
-
类似地,原材料和工具对应编程中的数据和功能。如果不同作用的工具和原材料放在一起,很乱,比如椒盐和眼影放在一起,锅铲和眼线笔放在一起。所以把做菜的工具和材料,与化妆的材料和工具,分别整合后分开装到容器里,要化妆的时候使用化妆的容器,做菜的时候使用做菜的容器
-
我们之前学的模块,把不同用处的功能和数据分开成多个文件,其实就是用面向对象的思想编程,模块就是一个对象。但是用文件整合功能单位比较大,希望有一种别的方式
-
字典中可以放数据,函数名,一个字典当作一个容器,里面放相关功能和数据,也是一种方法,但是字典里的函数还是要在字典之外定义,不能把函数体代码放进字典,还是不够精简,达不到真正整合的需求
在使用方法的时候,直接把字典传给这个方法,让方法去字典中找需要的数据和功能
python中有语法允许我们将数据和功能很好地整合到一起
类介绍
- 两个具有相同属性的对象,其中有不一样的数据,也有一样的数据。使用多个具有相同属性的对象,不需要存多个同样的数据:使用“类”
- 类也是容器,该容器用来存放同类对象共有的数据和功能
- 必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)。产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象
面向对象的语法
先定义类,再调用类产生对象
类语法
定义类
-
定义方式:
class 类名:
-
类最常用的是定义数据和功能,但其实可以包含任何其他代码
-
类体代码在定义阶段就会运行,会产生类的名称空间,名称空间中放类中的各种名字
# 命名类,使用驼峰体 class Student: # 变量的定义 stu_school = 'boy' # 功能的定义,传入一个对象,可以使用对象中的数据,对象可以是外部传入的 def show_info(stu_obj): pass print('=====')
-
使用
类名.__dict__
可以以字典形式查看类中的所有名字通过
类名.__dict__[名字]
操作字典的方式得到类字典中的名字和功能 -
python提供的语法,使用点
.
去访问类# 访问数据属性 print(Sudent.school_name) # 访问函数属性 print(Sudent.show_info)
调用类
调用类产生对象,将类实例化,拿到的返回值就是程序中的对象
stu1_obj = Student()
print(stu_obj.__dict__)
# {}
-
调用类不是执行类体代码,而是制造一个关联,此时对象中只有类中公有的数据和功能,没有独立的数据和功能,使用
__dict__
看到的是空字典 -
可以操作字典或使用点,为对象添加独有的功能和数据,添加后
__dict__
看到的是独有的内容class Students: school = 'new east' def cook(obj): print(f'My name is {obj.name}, my schol is {Students.school}') deimos = Students() deimos.name = 'deimos' print(deimos.__dict__) #{'name': 'deimos'}
出现问题
创建多个对象,多个对象需要添加多种同类的数据,比如每个学生对象,添加他们的年龄和名字,名字各不相同,但是纪录名字的变量名都是 student_name,student_age,为每个学生的每个名字和年龄都操作字典麻烦,需要改进
改进一:使用函数初始化
sut1_obj = Student()
sut2_obj = Student()
# 创建空对象
def init(obj1,x,y,z):
# 为空对象传入独有的属性
obj.stu_name = x
obj.stu_age = y
obj.stu_sex = z
init(stu1_obj,'deimos',21,'male')
init(stu2_obj,'aaa',21,'male')
改进二:调用类产生对象的时候自动调用init产生独有属性的功能,把init放到类里面去,前后加上下划线
- 只要在类中定义
__init__()
方法,python会在调用类的时候自动触发执行 - 其中
__init__
会自动将调用类时产生的空对象作为第一个参数传进来,于是我们只需要传init中的其他参数
于是,对于改进一,把__init__
西写在类中,调用的时候传入init需要的其他参数
class Students:
school = 'New east'
def __init__(obj,name,age):
obj.name = name
obj.age = age
def cook(obj):
print(f'My name is {obj.name}, my schol is {Students.school}, my age is {obj.age}')
deimos = Students('deimos',18)
# 在生成对象的时候就传入独有的参数
deimos.cook()
aaa = Students('aaa',20)
aaa.cook()
# My name is deimos, my schol is New east, my age is 18
# My name is aaa, my schol is New east, my age is 20
调用类的过程
调用类的过程又称为实例化,发生了三件事
- 先产生一个空对象
- python会自动调用类中的
__init__
方法,将空对象以及调用类时括号内传入的参数一并传给__init__
- 返回初始化完的对象
**总结__init__ 方法 **
- 会在调用类时自动触发执行,用来为对象初始化自己独有的数据
- init 内应该存放的是为对象初始化属性的功能,但是也可以放其他任意代码,例如想要在调用类的时候就立刻执行的代码
- init 默认返回None,也必须返回none
属性查找问题
查找对象中的数据,先从对象本身找,找不到则去类中找。查找类中的数据,访问到的是变量值,查找函数,找到的是函数的内存地址
数据属性
其实类中的东西都是给对象用的,同一类下共享的数据属性,在不同对象下访问的地址都一样
- 类中本身的值一变,所有都对象的这个值都一起变
- 本身不是类的值,而是通过init传进的独有的值,通过一个对象改变,则只能改变当前对象的
例:计算实例化的次数
# 每次实例化,都运行了一次__init__
class atudent:
count = 0
def __init__(obj):
atudent.count += 1
# 改变Student的值,而不是obj的值
deimos = atudent()
print(deimos.count)
函数属性
类的函数属性是给对象用的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
Student.show_info(stu1_obj)
# 可以这样写,相当于使用一个普通函数,stu1作为参数传入,相当于普通函数
# <function ...>
stu1_obj.show_info()
# 使用student1对象所在类下的公共方法,会被python识别为绑定方法,与普通函数不一样
# <bound ...>
绑定方法的特殊之处在于,谁来绑定方法就会将谁作为第一个参数自动传入。所以在类中定义的函数,一定要有一个参数 obj,用来接收绑定时候传进来的对象。如果定义的时候没有这个参数,相当于有实参没形参,会报错
在类里面定义函数,一定要有一个参数,约定设为self,用于接收自动传入的对象
class Student:
def show_info(self):
pass
之前在学点方法的时候,遇到self,当它不存在,因为self是自动传进来的参数,被谁点绑定就是作用于谁,如list.append(item),相当于list.append(self,item)
总结
对于扩展性高的部分,可以使用面向对象,对于其他普通部分不一定要使用类对象