面对对象
1、引入
假如我们是《英雄联盟》这款游戏的开发人员,我们现在要创造几个英雄(流浪法师·瑞兹、卡牌大师·崔斯特、探险家·伊泽瑞尔)、几个怪兽(大龙、小龙)
1.1、按照面向过程的想法
1、定义瑞兹
def Ryze():
data={
'nick':'流浪法师',
'name':'瑞兹',
'hp':1000,
'ap':80
}
print (data)
Ryze()
# {'nick': '流浪法师', 'name': '瑞兹', 'hp': 1000, 'ap': 80}
2、定义卡牌
def Twisted():
data={
'nick':'卡牌大师',
'name':'崔斯特',
'hp':600,
'ap':80
}
print (data)
Twisted()
# {'nick': '卡牌大师', 'name': '崔斯特', 'hp': 600, 'ap': 80}
3、定义ez
def Ezreal():
data={
'nick':'探险家',
'name':'伊泽瑞尔',
'hp':700,
'ap':80
}
print (data)
Ezreal()
# {'nick': '探险家', 'name': '伊泽瑞尔', 'hp': 700, 'ap': 80}
4、定义大龙
def Bigdargon():
data={
'name':'大龙',
'hp':2000,
'ad':30
}
print (data)
Bigdargon()
# {'name': '大龙', 'hp': 2000, 'ad': 30}
5、定义小龙
def Littledargon():
data={
'name':'小龙',
'hp':1000,
'ad':20
}
print (data)
Littledargon()
# {'name': '小龙', 'hp': 1000, 'ad': 20}
如果有更多的英雄或者怪兽,就要这么一直持续下去,非常的麻烦
1.2、按照面向对象的想法
1、定义一个英雄的模板(模子)
def hero(nick,name,hp,ad):
data={
'nick':nick,
'name':name,
'hp':hp,
'ad':ad
}
print (data)
2、根据这个英雄的模板(模子)创建瑞兹(实例化成具体的英雄)
Ryze=hero('流浪法师','瑞兹',1000,800)
# {'nick': '流浪法师', 'name': '瑞兹', 'hp': 1000, 'ad': 800}
3、创建卡牌
Twisted=hero('卡牌大师','崔斯特',600,80)
# {'nick': '卡牌大师', 'name': '崔斯特', 'hp': 600, 'ad': 80}
4、创建EZ
Ezreal=hero('探险家','伊泽瑞尔',700,80)
# {'nick': '探险家', 'name': '伊泽瑞尔', 'hp': 700, 'ad': 80}
5、定义一个怪兽的模板(模子)
def monster(name,hp,ad):
data={
'name':name,
'hp':hp,
'ad':ad
}
print (data)
6、根据这个怪兽的模板(模子)创建大龙(实例化成具体的怪兽)
Bigdargon=monster('大龙',2000,30)
# {'name': '大龙', 'hp': 2000, 'ad': 30}
7、创建小龙
Littledargon=monster('小龙',1000,20)
# {'name': '小龙', 'hp': 1000, 'ad': 20}
以上就是面向过程与面向对象一个对比。但是在实际的程序中,我们不会用函数来完成这个模板,而是用 类 来完成这个模板。
1.3、面向过程 VS 面向对象
面向过程的程序设计的核心是过程(流水线式思维或执行者思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
-
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
-
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
面向对象的程序设计的核心是对象(上帝式思维或设计者思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
-
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
-
缺点是:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。
1.4、一些基本的概念
- 类:用来描述具有相同的属性和方法的对象的集合,可以理解为是一个模板,比如上面的示例中,英雄是一个类
- 对象:类并不能直接使用,通过类创建出的实例(又称对象)才能使用,如利用英雄类,实例化成一个“流浪法师·瑞兹”,这个过程也叫 实例化
- 属性:类中的所有变量称为属性。如:英雄的名称、昵称、血量、物理攻击力、法术攻击力
- 方法:类中的所有函数通常称为方法。不过,和函数所有不同的是,类方法至少要包含一个 self 参数(后续会做详细介绍)
2、关于类
上面的示例,我们统一用函数来定义一个英雄模板。实际的工程里面,我们都是用”类”来表示的。那么如何定义一个类呢?
2.1、一个简单的类
class Hero:
nick='探险家'
name='伊泽瑞尔'
hp=700
ad=80
def attack(self):
print("{0}·{1}正在攻击他人,给他人造成{2}点的伤害".format(self.nick,self.name,self.ad))
说明
1、class Hero: —— 创建类,类名为Hero。类名我们一般约定俗成的,使用大驼峰(每个单词首字母大写)风格命名
2、nick、name、hp、ad —— 这些是属性,且是静态属性
3、def attack(self): —— 在类里面定义一个方法,第一个参数通常是self,self表示实例后的具体对象
2.2、访问属性与方法
实例化之前
Hero.nick # 查看类的属性
# 结果是:'探险家'
Hero.attack # 查看类的方法,注意这里不是调用方法。调用方法需要先实例化
# 结果是:<function __main__.Hero.attack(self)>
实例化后
# 实例成对象
ez=Hero()
ez.nick # 查看类的属性
# 结果是:'探险家'
ez.attack() # 调用方法
# 结果是:探险家·伊泽瑞尔正在攻击他人,给他人造成80点的伤害
2.3、动态属性
# 动态添加属性
ez.type='shooter' # 英雄类型是射手
ez.type # 获取属性
# 结果是:'shooter'
# 删除属性
del ez.type
2.4、构造方法
在上面我们定义的类,已经把nick、name等属性写死了,也就是说上面定义的类,只适用于EZ,并不是我们想要的英雄模板。那能不能在定义类的时候传入参数,定义不同的nick、name等参数,从而真正成为一个英雄的模板呢?这里就需要用到构造函数__init()__
__init__()
方法可以包含多个参数,但必须包含一个名为 self 的参数,且必须作为第一个参数。也就是说,类的构造方法最少也要有一个 self 参数,self表示当前的实例
class Hero:
def __init__(self,nick,name,hp,ad): # 构造函数,self表示当前的实例
self.nick=nick
self.name=name
self.hp=hp
self.ad=ad
def attack(self):
print("{0}攻击了对方,给对方造成{1}伤害".format(self.name,self.ad))
实例化为EZ对象
Ezreal=Hero('探险家','伊泽瑞尔',700,80)
Ezreal.attack()
# 结果是:伊泽瑞尔攻击了对方,给对方造成80伤害
实例化为卡牌对象
Twisted=Hero('卡牌大师','崔斯特',600,80)
Twisted.attack()
# 结果是:崔斯特攻击了对方,给对方造成80伤害
类实例化后,会在内存中开辟一个存储空间,不同的实例化后的对象,内存不一样。
print(id(Ezreal))
# 结果是:2841153837616
print(id(Twisted))
# 结果是:2841160233840