本节内容:
面向对象的编程初级
楔子:
假如你现在是一家游戏公司的开发人员,现在需要你开发一个叫《人狗大战》的游戏,那么你就想啊想,人狗大战emmm...起码要两个角色吧,一个人,一个狗,而且人和狗攻击方式应该不同吧,比如狗咬人,人捡东西打狗,但是怎么描述这种不同角色和他们的功能呢?
绞尽脑汁,用尽毕生所学终于写出来下面代码来描述这两个角色
1 def person(name,age,sex,job):
2 data = {
3 'name':name,
4 'age':age,
5 'sex':sex,
6 'job':job
7 }
8
9 return data
10
11 def dog(name,dog_type):
12 data = {
13 'name':name,
14 'type':dog_type
15 }
16 return data
上面两个方法相当于造了两个模子,游戏开始,你得生成一个人和狗的实际对象吧,怎么生成呢?
1 d1 = dog("木子园","藏獒")
2
3 p1 = person("肥肥",12,"不详","运维")
4
5 p2 = person("月月鸟",16,"不详","Teacher")
两个角色对象生成了,狗和人还有不同的功能呀,狗会咬人,人会打狗,对不对? 怎么实现呢,。。想到了, 可以每个功能再写一个函数,想执行哪个功能,直接 调用 就可以了,对不?
1 def bark(d):
2 print("dog %s:wang.wang..wang..."%d['name'])
3
4
5 def walk(p):
6 print("person %s is walking..." %p['name'])<br><br>
攻击技能
d1 = dog("木子园","藏獒")
p1 = person("肥肥",12,"不详","运维")
p2 = person("月月鸟",16,"不详","Teacher")
walk(p1) bark(d1)
上面的功能实现的简直是完美!
但是仔细玩耍一会,你就不小心干了下面这件事
1 p1 = person("肥肥",12,"不详","运维")
2 bark(p1) #把人的对象传给了狗的方法
事实 上,这并没出错。很显然,人是不能调用狗的功能的,如何在代码级别实现这个限制呢?
1 def person(name,age,sex,job):
2 def walk(p):
3 print("person %s is walking..." % p['name'])
4
5 data = {
6 'name':name,
7 'age':age,
8 'sex':sex,
9 'job':job,
10 'walk':walk
11 }
12
13 return data
14
15 def dog(name,dog_type):
16
17
18 def bark(d):
19 print("dog %s:wang.wang..wang..."%d['name'])
20 data = {
21 'name':name,
22 'type':dog_type,
23 'bark':bark
24 }
25
26 return data
d1 = dog("木子园","藏獒")
p1 = person("肥肥",12,"不详","运维")
p2 = person("月月鸟",16,"不详","Teacher")
d1['bark'](p1) #把人的对象传给了狗的方法
面向过程vs面向对象
编程规范:
编程时程序员用特定语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一个程序是程序员你为了得到一个任务结果二编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式,对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。不同的编程范式 本质上代表对各种类型的任务采取的不同的解决思路,大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。两种最重要的编程范式分别是面向过程的编程和面向对象的编程。
面向过程编程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向过程编程依赖 - 你猜到了- procedures,一个procedure包含一组要被进行计算的步骤, 面向过程又被称为top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
代码如下
1 def db_conn():
2 print("connecting db...")
3
4
5 def db_backup(dbname):
6 print("导出数据库...",dbname)
7 print("将备份文件打包,移至相应目录...")
8
9 def db_backup_test():
10 print("将备份文件导入测试库,看导入是否成功")
11
12
13 def main():
14 db_conn()
15 db_backup('my_db')
16 db_backup_test()
17
18
19
20 if __name__ == '__main__':
21 main()
这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 举个例子,如果程序开头你设置了一个变量值 为1 , 但如果其它子过程依赖这个值 为1的变量才能正常运行,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程 , 那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高。
所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。
面向对象的编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
创建类和对象
面向对象编程时一种编程方式,此编程方式的落地需要使用"类"和"对象"来实现,所以,面向对象编程其实就是对"类"和"对象"的使用。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
对象则是根据创建的实例,通过实例对象可以执行类中的函数
- class是关键字,表示创建的是类
- 创建对象,类名称后边加括号即可
ps:类中的函数第一个参数必须是self(详细见:类的三大特性之封装)
类中定义的函数叫做"方法"
1 #创建类
2 class foo:
3 def bar(self):
4 print('bar')
5
6 def hello(self, name):
7 print("i is %s" % name)
8
9 #根据foo类创建对象obj
10 obj = foo()
11 obj.bar() #执行bar方法
12 obj.hello('铁棍') #执行hello方法
到这里大家是不是就有疑问了?使用函数式编程和面向对象编程方式来执行一个"方法"时函数要比面向对象简便
- 面向对象:【创建对象】【通过对象执行方法】
- 函数编程:【执行函数】
观察上述对比答案则是肯定的,然后并非绝对,场景的不同适合其的编程方式也不同。
总结:函数式的应用场景--》各个函数之间是独立且无功用数据
面向对象的三大特性
面向对象的三大特性指的是:封装、继承 和 多态。
1、封装
封装顾名思义就是将内容封装到某个地方,以后在去调用被封装在某处的内容。所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
第一步:将内容封装到某处
self是一个形式参数,当执行obj1 = foo(‘muzizhu’,18)时,self等于obj1l;当执行obj2 = foo('zhubao',18)时,self等于obj2
所以,内容其实被封装到了对象obj1和obj2中,每个对象中都有name 和age属性,在内存里类似于下图来保存。
第二步:从某处调用被封装的内容
调用被封转的内容时,有两种情况:
- 通过对象调用
- 通过self间接调用
1、通过对象直接掉用呗封装的内容
上图展示了对象obj1和obj2在内存中的保存方式,根据报存格式可以如此调用被封装的内容:对象.属性名
class foo:
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = foo('muzizhu', 18)
print(obj1.age) # 直接调用obj1对象的name属性
print(obj1.name) # 直接调用obj1对象的age属性
obj2 = foo('zhubao', 18)
print(obj2.name) # 直接调用obj2对象的name属性
print(obj2.age) # 直接调用obj2对象的age属性
2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接掉用被封装的内容
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print self.name
print self.age
obj1 = Foo('zhuzhu', 18)
obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 zhuzhu ;self.age 是 18
obj2 = Foo('zhubao', 19)
obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 zhubao; self.age 是 19
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
练习一:在终端输出如下信息
- 小明,10岁,男,上山去砍柴
- 小明,10岁,男,开车去东北
- 老李,90岁,男,上山去砍柴
- 老李,90岁,男,开车去东北
1 def kanchai(name, age, gender): 2 print "%s,%s岁,%s,上山去砍柴" %(name, age, gender)
3
4
5 def qudongbei(name, age, gender):
6 print "%s,%s岁,%s,开车去东北" %(name, age, gender)
7
8
9 def dabaojian(name, age, gender):
10 (name, age, gender)
11
12
13 kanchai('小明', 10, '男')
14 qudongbei('小明', 10, '男')
15 dabaojian('小明', 10, '男')
16
17
18 kanchai('老李', 90, '男')
19 qudongbei('老李', 90, '男')
20 dabaojian('老李', 90, '男')
函数式编程
1 class Foo: 2 3 def __init__(self, name, age ,gender):
4 self.name = name
5 self.age = age
6 self.gender = gender
7
8 def kanchai(self):
9 print "%s,%s岁,%s,上山去砍柴" %(self.name, self.age, self.gender)
10
11 def qudongbei(self):
12 print "%s,%s岁,%s,开车去东北" %(self.name, self.age, self.gender)
13
14 def dabaojian(self):
15 print "%s,%s岁,%s, %(self.name, self.age, self.gender)
16
17
18 xiaoming = Foo('小明', 10, '男')
19 xiaoming.kanchai()
20 xiaoming.qudongbei()
21 xiaoming.dabaojian()
22
23 laoli = Foo('老李', 90, '男')
24 laoli.kanchai()
25 laoli.qudongbei()
26 laoli.dabaojian()
面向对象编程
上述对比可以看出,如果使用函数式编程,需要在每次执行函数时传入相同的参数,如果参数多的话,又需要粘贴复制了... ;而对于面向对象只需要在创建对象时,将所有需要的参数封装到当前对象中,之后再次使用时,通过self间接去当前对象中取值即可。
练习二:游戏人生程序
1、创建三个游戏人物,分别是:
- 苍井井,女,18,初始战斗力1000
- 东尼木木,男,20,初始战斗力1800
- 波多多,女,19,初始战斗力2500
2、游戏场景,分别:
- 草丛战斗,消耗200战斗力
- 自我修炼,增长100战斗力
- 多人游戏,消耗500战斗力
1 # -*- coding:utf-8 -*- 2
3 # ##################### 定义实现功能的类 #####################
4
5 class Person:
6
7 def __init__(self, na, gen, age, fig):
8 self.name = na
9 self.gender = gen
10 self.age = age
11 self.fight =fig
12
13 def grassland(self):
14 """注释:草丛战斗,消耗200战斗力"""
15
16 self.fight = self.fight - 200
17
18 def practice(self):
19 """注释:自我修炼,增长100战斗力"""
20
21 self.fight = self.fight + 200
22
23 def incest(self):
24 """注释:多人游戏,消耗500战斗力"""
25
26 self.fight = self.fight - 500
27
28 def detail(self):
29 """注释:当前对象的详细情况"""
30
31 temp = "姓名:%s ; 性别:%s ; 年龄:%s ; 战斗力:%s" % (self.name, self.gender, self.age, self.fight)
32 print temp
33
34
35 # ##################### 开始游戏 #####################
36
37 cang = Person('苍井井', '女', 18, 1000) # 创建苍井井角色
38 dong = Person('东尼木木', '男', 20, 1800) # 创建东尼木木角色
39 bo = Person('波多多', '女', 19, 2500) # 创建波多多角色
40
41 cang.incest() #参加一次多人游戏
42 dong.practice()#东尼木木自我修炼了一次
43 bo.grassland() #波多多参加一次草丛战斗
44
45
46 #输出当前所有人的详细情况
47 cang.detail()
48 dong.detail()
49 bo.detail()
50
51
52 cang.incest() #又参加一次多人游戏
53 dong.incest() #东尼木木也参加了一个多人游戏
54 bo.practice() #波多多自我修炼了一次
55
56 #输出当前所有人的详细情况
57 cang.detail()
58 dong.detail()
59 bo.detail()
游戏人生
2、继承
继承,面向对象中的继承和现实中的继承相同,即:儿子可以继承父亲的内容。
例子:
猫可以:喵喵喵、吃、喝、拉、撒
狗可以:汪汪汪、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫和狗实现他们的功能,如下所示:
1 class 猫: 2
3 def 喵喵叫(self):
4 print '喵喵叫'
5
6 def 吃(self):
7 # do something
8
9 def 喝(self):
10 # do something
11
12 def 拉(self):
13 # do something
14
15 def 撒(self):
16 # do something
17
18 class 狗:
19
20 def 汪汪叫(self):
21 print '喵喵叫'
22
23 def 吃(self):
24 # do something
25
26 def 喝(self):
27 # do something
28
29 def 拉(self):
30 # do something
31
32 def 撒(self):
33 # do something
伪代码
这就不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别在猫和狗的类中写了两次。如果使用继承思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵喵(猫继承动物的功能)
狗:汪汪汪(狗继承动物的功能)
class 动物: def 吃(self): # do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):
def 喵喵叫(self):
print '喵喵叫'
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):
def 汪汪叫(self):
print '喵喵叫'
伪代码
class 动物: def 吃(self): # do something def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):
def 喵喵叫(self):
print '喵喵叫'
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):
def 汪汪叫(self):
print '喵喵叫'
复制代码
复制代码
class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '猫'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 执行 #########
c1 = Cat('小白家的小黑猫')
c1.eat()
c2 = Cat('小黑的小白猫')
c2.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
伪代码实例
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需要继承父类二不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过派生类和基类,他们于子类和父类只是叫法不同而已。
学习了继承的写法之后,我们用代码来实现上述猫狗的功能:
1 class Animal:
2
3 def eat(self):
4 print "%s 吃 " %self.name
5
6 def drink(self):
7 print "%s 喝 " %self.name
8
9 def shit(self):
10 print "%s 拉 " %self.name
11
12 def pee(self):
13 print "%s 撒 " %self.name
14
15
16 class Cat(Animal):
17
18 def __init__(self, name):
19 self.name = name
20 self.breed = '猫'
21
22 def cry(self):
23 print '喵喵叫'
24
25 class Dog(Animal):
26
27 def __init__(self, name):
28 self.name = name
29 self.breed = '狗'
30
31 def cry(self):
32 print '汪汪叫'
33
34
35 # ######### 执行 #########
36
37 c1 = Cat('小白家的小黑猫')
38 c1.eat()
39
40 c2 = Cat('小黑的小白猫')
41 c2.drink()
42
43 d1 = Dog('胖子家的小瘦狗')
44 d1.eat()
完整代码
简单的继承这就说完了,但是真的就真么简单吗,哈哈对,如果有两个父亲怎么办呢?而且恰巧这两个父亲有同样名字的方法,这是你要用哪个?
1、python的类中可以继承多个类
2、python的类如果继承了多个类,那么其寻找方法的方式由两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了更多的功能,也是之后推荐的写法,从写法上区分的话,如果当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
class D:
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
经典类的多继承
class D(object):
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
新式类的多继承
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
3、多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”
1 class F1:
2 pass
3
4
5 class S1(F1):
6
7 def show(self):
8 print 'S1.show'
9
10
11 class S2(F1):
12
13 def show(self):
14 print 'S2.show'
15
16
17 # 由于在Java或C#中定义函数参数时,必须指定参数的类型
18 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
19 # 而实际传入的参数是:S1对象和S2对象
20
21 def Func(F1 obj):
22 """Func函数需要接收一个F1类型或者F1子类的类型"""
23
24 print obj.show()
25
26 s1_obj = S1()
27 Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
28
29 s2_obj = S2()
30 Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
python伪代码实现Java或C#的多态
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
def Func(obj):
print obj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
python的"鸭子类型"
小结:
以上就是本节对于面向对象初级知识的介绍,总结如下:
- 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
- 类 是一个模板,模板中包装了多个“函数”供使用
- 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
- 面向对象三大特性:封装、继承和多态
面向对象中的小知识
1、什么样的代码才是面向对象?
答:从简单来说,如果程序中的所有功能都是用 类 和 对象 来实现,那么就是面向对象编程了。
2、函数式编程 和 面向对象 如何选择?分别在什么情况下使用?
答:须知:对于 C# 和 Java 程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于 Python 和 PHP 等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。
所以,一般在Python开发中,全部使用面向对象 或 面向对象和函数式混合使用
面向对象的应用场景:
- 多函数需要使用共同的值,如:数据库的增、删、改、查操作都需要链接数据库字符串、主机名、用户名和密码
class SqlHelper:
def __init__(self, host, user, pwd):
self.host = host
self.user = user
self.pwd = pwd
def 增(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 删(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 改(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 查(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接# do something
- 例子
- 需要创建多个事物,每个事物属性个数相同,但是值的需求不同
如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
class Person:
def __init__(self, name ,age ,blood_type):
self.name = name
self.age = age
self.blood_type = blood_type
def detail(self):
temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type)
print temp
zhangsan = Person('张三', 18, 'A')
lisi = Person('李四', 73, 'AB')
yangwu = Person('杨五', 84, 'A')
- View Code
3、类和对象在内存中是如何保存?
答:类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:
如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过 obj1 执行 【方法一】 时,过程如下:
- 根据当前对象中的 类对象指针 找到类中的方法
- 将对象 obj1 当作参数传给 方法的第一个参数 self