面向对象
1 面向过程与面向对象
1.1 面向过程编程与面向对象编程对比
1、面向过程编程
核心是过程二字,过程指的是解决的步骤,即先干啥、再干啥、后干啥
基于该思想写程序就在设计一条条的流水线优点:复杂的问题流程化、进而简单化
缺点:牵一发而动全身,扩展差2、面向对象编程
核心是“对象”二字,对象指的是盛放相关的数据与功能的容器
基于该思想编写程序就在创造一个个的容器来把相关的东西盛到一起优点:扩展性强
缺点:加大了编程的复杂度
类是用来解决对象之间代码冗余问题的
2 类与对象
2.1 类定义阶段发生的三件事
- 1、会执行类体的代码
- 2、会产生一个类的名称空间,用来将类体代码运行过程中产生的名字都丢进去
- 3、将名称空间的内存地址绑定给类名
2.2 调用类阶段发生的三件事
- 1、会创建空对象(调用
__new__
方法来创建的空对象) - 2、会自动触发类中
__init__
函数的运行,__init__
(空对象,,”egon”,18,”male”),完成对象的初始化操作,注意:__init__
不能有返回值 - 3、返回该对象,赋值给stu1_dic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130# 版本1---------------------------------------------
stu1_name = "egon"
stu1_age = 18
stu1_gender = "male"
stu1_courses = []
stu2_name = "tom"
stu2_age = 38
stu2_gender = "female"
stu2_courses = []
def choose_course(stu_name,stu_courses,course):
stu_courses.append(course)
print('学生 %s 选课成功 %s' %(stu_name,stu_courses))
choose_course(stu1_name,stu1_courses,"python+go开发")
choose_course(stu2_name,stu2_courses,"linux运维")
# # 版本2----------------------------------------------
stu1_dic = {
'name':"egon",
"age":18,
'gender':"male",
"Course":[]
}
stu2_dic = {
'name':"tom",
"age":38,
'gender':"female",
"Course":[]
}
def choose_course(stu_dic,course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' %(stu_dic["name"],stu_dic["Course"]))
choose_course(stu1_dic,"python+go开发")
choose_course(stu2_dic,"linux运维")
# 版本3
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
stu1_dic = {
"school": "SH",
'name': "egon",
"age": 18,
'gender': "male",
"Course": [],
'choose_course': choose_course
}
stu2_dic = {
"school": "SH",
'name': "tom",
"age": 38,
'gender': "female",
"Course": [],
'choose_course': choose_course
}
stu1_dic['choose_course'](stu1_dic, "python+go开发") # 谁来调用就传自己
stu2_dic["choose_course"](stu2_dic, "linux运维")
# 版本4
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
Student = {
"school": "SH",
'choose_course': choose_course
}
stu1_dic = {
'name': "egon",
"age": 18,
'gender': "male",
"Course": [],
'Student':Student
}
stu2_dic = {
'name': "tom",
"age": 38,
'gender': "female",
"Course": [],
'Student':Student
}
# 类是用来解决对象之间代码冗余问题的
class Student:
school = "SH"
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
print(Student.__dict__)
# 调用类
stu1_dic = Student()
stu2_dic = Student()
print(stu1_dic.__dict__)
print(stu2_dic.__dict__)
print(stu1_dic.school) # stu1_dic.__dict__["school"]
print(stu2_dic.school) # stu2_dic.__dict__["school"]
# if "school" in stu1_dic.__dict__:
# print(stu1_dic.__dict__["school"])
# else:
# print(Student.__dict__['school'])2.3 初始化构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90# 版本1---------------------------------------------
class Student:
school = "SH"
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
# 调用类
stu1_dic = Student()
stu2_dic = Student()
stu1_dic.name = "egon" # stu1_dic.__dict__["name"] = "egon"
stu1_dic.age = 18 # stu1_dic.__dict__["age"] = 18
stu1_dic.gender = "male" # stu1_dic.__dict__["gender"] = "male"
stu1_dic.courses = [] # stu1_dic.__dict__["Course"] = []
stu2_dic.name = "tom"
stu2_dic.age = 38
stu2_dic.gender = "female"
stu2_dic.courses = []
print(stu1_dic.__dict__)
print(stu2_dic.__dict__)
print(Student.__dict__)
print(stu1_dic.name)
# 版本2:---------------------------------------------------------------
class Student:
school = "SH"
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
# 类定义阶段发生的三件事
# 1、会执行类体的代码
# 2、会产生一个类的名称空间,用来将类体代码运行过程中产生的名字都丢进去
# 3、将名称空间的内存地址绑定给类名
# 调用类
stu1_dic = Student()
stu2_dic = Student()
def init(stu_dic, name, age, gender, courses=None):
if courses is None:
courses = []
stu_dic.name = name
stu_dic.age = age
stu_dic.gender = gender
stu_dic.courses = courses
init(stu1_dic,"egon",18,"male")
init(stu2_dic,"tom",38,"female")
print(stu1_dic.__dict__)
print(stu2_dic.__dict__)
print(Student.__dict__)
# 版本3:-----------------------------------------------------------------
class Student:
school = "SH"
# 空对象,,"egon",18,"male")
def __init__(stu_dic, name, age, gender, courses=None):
if courses is None:
courses = []
stu_dic.name = name
stu_dic.age = age
stu_dic.gender = gender
stu_dic.courses = courses
# return None
def choose_course(stu_dic, course):
stu_dic["Course"].append(course)
print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))
stu1_dic = Student("egon",18,"male")
stu2_dic = Student("tom",38,"female")
print(stu1_dic.__dict__)
print(stu2_dic.__dict__)
print(Student.__dict__)2.4 属性查找
- 类有两种属性*
- 1、数据属性
- 2、函数属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68class Student:
school = "SH"
def __init__(self, name, age, gender, courses=None):
if courses is None:
courses = []
self.name = name
self.age = age
self.gender = gender
self.courses = courses
# return None
def choose_course(self, course):
self.courses.append(course)
print('学生 %s 选课成功 %s' % (self.name,self.courses))
def func(self):
pass
stu1_obj = Student("egon",18,"male")
stu2_obj = Student("tom",38,"female")
# 类中有两种属性--------------------------------------
# 1、数据属性
print(Student.school)
# 2、函数属性
print(Student.choose_course)
Student.choose_course(stu1_obj,"python+go开发")
# 但是类中的属性其实是为对象准备----------------------------------
# 1、类的属性是直接共享给所有对象用的
print(Student.school,id(Student.school))
print(stu1_obj.school,id(stu1_obj.school))
print(stu2_obj.school,id(stu2_obj.school))
Student.school = "XXX"
print(stu1_obj.school)
print(stu2_obj.school)
stu1_obj.school = "XXX"
print(stu1_obj.school)
print(stu2_obj.school)
print(Student.school)
# 2、类的函数属性是绑定给对象用的
print(Student.choose_course)
print(stu1_obj.choose_course)
print(stu2_obj.choose_course)
stu1_obj.choose_course("python+go开发") # Student.choose_course(stu1_obj,"python+go开发")
stu2_obj.choose_course("linux运维") # Student.choose_course(stu2_obj,"linux运维")
Student.func()
stu1_obj.func()
# stu1_obj.choose_course("python+go开发")
# python3统一了类与类型的概念
l1 = [11,22,33] # l1 = list([11,22,33])
# l1.append(44) # list.append(l1,44)
print(type(l1))
print(type(stu1_obj))3 绑定方法与非绑定方法
3.1 类中定义的函数分为两大类
- 绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数自动传入
- 绑定给类的方法
- 绑定给对象的方法
- 非绑定方法:不与任何人绑定,意味着谁都可以来调用,但是无论谁来调用就是一个普通函数,没有自动传参的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import settings
class Mysql:
def __init__(self, ip, port):
self.id = self.create_id()
self.ip = ip
self.port = port
def tell_info(self):
print("<%s:%s>" % (self.ip, self.port))
# 将方法与类绑定,专门给类用,自动传入的参数cls任然是类
def from_conf(cls):
return cls(settings.IP, settings.PORT)
# 非绑定方法,又称作非绑定方法
def create_id():
import uuid
return uuid.uuid4()
#obj = Mysql('1.1.1.1',3306)
#obj.tell_info()
# obj = Mysql.from_conf()
# print(obj.__dict__)
# print(Mysql.from_conf)
# print(Mysql.create_id)
# print(obj.create_id)
print(obj.__dict__)
3.2 总结绑定方法与非绑定方法的使用
若类中需要一个功能,该功能的实现代码中需要引用对象则将其定义成对象放方法、需要引用类将其定义成类方法、无需引用类或对象则将其定义成静态方法
4 隐藏属性
1 | # 隐藏属性的真正目的就是为了不让使用者在类外部的直接操作属性 |
总结:
__开头的属性隐藏有四个特点:
- 1、并不是真正的隐藏,只是一种语法意义上的变形
- 2、该变形操作只在类定义阶段扫描语法时发生一次,类定义之后
__
开头的属性不会变形 - 3、该隐藏对外不对内
- 隐藏属性的目的**
- 1、隐藏数据属性的目的:为了严格控制某一属性的操作
- 2、隐藏函数属性的母的:隔离复杂度
5 property
5.1 property的用途
可以将类中的函数“伪装成”对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果,例如:
1 | class People: |
另外property还提供设置和删除属性的功能,如下:
1 | # 案例2: |
6 继承
6.1 致命三问
- 1、什么是继承
继承是一种新建子类的方式,新建的类称之为子类/派生类,被继承的称之为父类/基类
子类会遗传父类的属性 - 2、为何要用继承
类是解决对象之间冗余问题的
继承可以解决类与类之间的冗余问题 - 3、如何继承
在python中支持多继承
在python3中如果一个类没有继承任何父类,那么默认继承object类
但凡是继承了object类的子类,以及该子类的子子孙孙类都能用到object内的功能,称之为新式类
没有继承了object类的子类,以及该子类的子子孙孙类都能用不到object内的功能,称之为经典类
ps:只有在python2中才区分经典类与新式类,python3中都是新式类
1 | class ParentClass1: #定义父类 |
6.2 继承解决冗余问题
1 | class People: |
6.3 在子类派生的新方法中如何重用父类
6.3.1 在子类派生的新方法中如何重用父类的功能
- 方式一:指名道姓地访问父类的函数,与继承无关
- 方式二:super(),严格依赖继承
1 | class People: |
6.4 在单继承背景下得属性查找
1 | class Foo: |
1 | # class Foo: |
6.5 在多继承背景下得属性查找
6.5.1 非菱形继承关系下的属性查找
在非菱形继承关系下,新式类与经典的属性查找属性都一样,都是一个分支一个分支地找下取
1 | class D: |
6.5.2 菱形继承关系下得属性查找
菱形继承/死亡钻石:一个类继承的多条分支最终汇聚到一个非object类上
- 新式类:广度优先
- 经典类:深度优先
1 | class G: |
总结:属性的查找顺序最终还是按照mro列表的顺序来查找
1 | # super()得到一个特殊的对象,该对象访问的属性直接从父类中查找 |
强调:属性查找参考的是属性查找发起者的mro列表
7 mixins机制
1 | # 继承表达的关系:is-a |
一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的,它仅仅只是代码层面的逻辑。不过有没有这种情况,一个类的确是需要继承多个类呢? 答案是有,我们还是拿交通工具来举例子: 民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的
1 | class Vehicle: # 交通工具 |
但是如果民航飞机和直升机都各自写自己的飞行fly方法,又违背了代码尽可能重用的原则(如果以后飞行工具越来越多,那会重复代码将会越来越多)。 怎么办???为了尽可能地重用代码,那就只好在定义出一个飞行器的类,然后让民航飞机和直升飞机同时继承交通工具以及飞行器两个父类,这样就出现了多重继承。这时又违背了继承必须是”is-a”关系。这个难题该怎么解决?
Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属”is-a”关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下:
1 | class Vehicle: # 交通工具 |
可以看到,上面的CivilAircraft、Helicopter类实现了多继承,不过它继承的第一个类我们起名为FlyableMixin,而不是Flyable,这个并不影响功能,但是会告诉后来读代码的人,这个类是一个Mixin类,表示混入(mix-in),这种命名方式就是用来明确地告诉别人(python语言惯用的手法),这个类是作为功能添加到子类中,而不是作为父类,它的作用同Java中的接口。所以从含义上理解,CivilAircraft、Helicopter类都只是一个Vehicle,而不是一个飞行器。
使用Mixin类实现多重继承要非常小心
- 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
- 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
- 然后,它不依赖于子类的实现
- 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
8 组合
1 | # 继承表达是is-a关系 |
9 多态
9.1 多态性
我们可以在不知道对象具体类型的前提嘻,而可以直接使用对象
1 | # 例如:__len__() 就是多态的典型应用之一 |
9.2 abstractmethod
@abc.abstractmethod # 表示继承它的子类必须自己实现这相应的方法,例如:
1 | import abc |
9.3 鸭子类型
简单来说,就是:只能叫得像鸭子,跑的像鸭子,你就是鸭子,例如:
1 | # python崇尚鸭子类型 |
10 内置方法
1 | # 在满足某种条件下会自动触发执行 |
其他内置方法(理解,暂时不需要掌握):https://www.cnblogs.com/linhaifeng/articles/6204014.html
11 反射
11.1 hasattr、setattr、getattr、delattr
反射:通过字符串来操作属性
1 | # -hasattr------------------------------ |