引子
假设我们要开发一个关于飞机大战的游戏,那么游戏的里面就会有两个角色,分别是属于玩家操控的战机和敌方的战机,并且两个战机都有不同的技能或攻击方式,现在我们用自己目前所学的去写出下面的这些代码:
def Fighter(name,atk,hp,speed):
fighter = {
'name' : name, #战机的名字
'atk' : atk, #战机的攻击力
'hp' : hp, #战机的血量
'speed' : speed #战机的速度
}
return fighter
def EnemyFighter(name,atk,hp,speed,kind):
enemyfighter = {
'name' : name, #敌机的名字
'atk' : atk, #敌机的攻击力
'hp' : hp, #敌机的血量
'speed' : speed, #敌机的速度
'kind' : kind #敌机的种类
}
return enemyfighter
我们在写好了这些属性之后就可以生成敌方的战机和玩家的战机:
playerFighter1 = Fighter('Player1',200,1500,300)
playerFighter2 = Fighter('Player2',150,2000,280)
Boss1 = EnemyFighter('lazerBoss',1000,7000,50,'Boss')
Monster1 = EnemyFighter('Monster1',100,200,150,'Monster')
print(playerFighter1) #{'name': 'Player1', 'atk': 200, 'hp': 1500, 'speed': 300}
print(playerFighter2) #{'name': 'Player2', 'atk': 150, 'hp': 2000, 'speed': 280}
print(Boss1) #{'name': 'lazerBoss', 'atk': 1000, 'hp': 7000, 'speed': 50, 'kind': 'Boss'}
print(Monster1) #{'name': 'Monster1', 'atk': 100, 'hp': 200, 'speed': 150, 'kind': 'Monster'}
那么在生成好了这些对象之后,我们还缺的是它们不同的攻击方法,因此我们要在它们的方法中再添加一个方法再去调用
def Fighter(name,atk,hp,speed): #定义一个战机的属性
fighter = {
'name' : name, #战机的名字
'atk' : atk, #战机的攻击力
'hp' : hp, #战机的血量
'speed' : speed, #战机的速度
}
def playerAttack(enemyfighter): #玩家攻击函数
enemyfighter['hp'] -= fighter['atk']
print('{0}被{1}攻击了,损失了{2}HP!'.format(enemyfighter['name'],fighter['name'],fighter['atk']))
fighter['playerAttack'] = playerAttack
return fighter
def EnemyFighter(name,atk,hp,speed,kind):
enemyfighter = {
'name' : name, #敌机的名字
'atk' : atk, #敌机的攻击力
'hp' : hp, #敌机的血量
'speed' : speed, #敌机的速度
'kind' : kind #敌机的种类
}
def enemyFighterAttack(fighter): #敌机攻击函数
fighter['hp'] -= enemyfighter['atk']
print('{0}被{1}攻击了,损失了{2}HP!'.format(fighter['name'],enemyfighter['name'],enemyfighter['atk']))
enemyfighter['enemyFighterAttack'] = enemyFighterAttack
return enemyfighter
playerFighter1 = Fighter('Player1',200,1500,300)
playerFighter2 = Fighter('Player2',150,2000,280)
Boss1 = EnemyFighter('lazerBoss',1000,7000,50,'Boss')
Monster1 = EnemyFighter('Monster1',100,200,150,'Monster')
print(playerFighter1) #{'name': 'Player1', 'atk': 200, 'hp': 1500, 'speed': 300, 'playerAttack': <function Fighter.<locals>.playerAttack at 0x0000000002138A60>}
print(playerFighter2) #{'name': 'Player2', 'atk': 150, 'hp': 2000, 'speed': 280, 'playerAttack': <function Fighter.<locals>.playerAttack at 0x0000000002138D08>}
print(Boss1) #{'name': 'lazerBoss', 'atk': 1000, 'hp': 7000, 'speed': 50, 'kind': 'Boss', 'enemyFighterAttack': <function EnemyFighter.<locals>.enemyFighterAttack at 0x0000000002138D90>}
print(Monster1) #{'name': 'Monster1', 'atk': 100, 'hp': 200, 'speed': 150, 'kind': 'Monster', 'enemyFighterAttack': <function EnemyFighter.<locals>.enemyFighterAttack at 0x0000000002138E18>}
现在调用这两个功能代码:
print(playerFighter1) #{'name': 'Player1', 'atk': 200, 'hp': 1500, 'speed': 300, 'playerAttack': <function Fighter.<locals>.playerAttack at 0x0000000002138A60>}
Boss1['enemyFighterAttack'](playerFighter1) #Player1被lazerBoss攻击了,损失了1000HP!
print(playerFighter1) #{'name': 'Player1', 'atk': 200, 'hp': 500, 'speed': 300, 'playerAttack': <function Fighter.<locals>.playerAttack at 0x00000000027A8A60>}
print(Monster1) #{'name': 'Monster1', 'atk': 100, 'hp': 200, 'speed': 150, 'kind': 'Monster', 'enemyFighterAttack': <function EnemyFighter.<locals>.enemyFighterAttack at 0x0000000002138E18>}
playerFighter2['playerAttack'](Monster1) #Monster1被Player2攻击了,损失了150HP!
print(Monster1) #{'name': 'Monster1', 'atk': 100, 'hp': 50, 'speed': 150, 'kind': 'Monster', 'enemyFighterAttack': <function EnemyFighter.<locals>.enemyFighterAttack at 0x00000000027A8E18>}
像上面Fighter和EnemyFighter函数都是定义了一类事物,直到我们调用了函数,赋值了之后才真的有了一个实实在在的事物
面向对象、面向过程、面向函数
面向对象:把整个任务封装成一个大的类,在这个类里面详细地分解执行每个步骤,只需要执行类就可以完成任务。优点是解决了程序的扩展性,对某一个对象单独修改,会立刻反映到整个体系中,比如对一个游戏中的一个人物参数的特征和技能修改都很容易。缺点是可控性差无法像面向过程的程序设计流水线式,可以很精准得预测问题的处理流程和结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预知结果。因此经常我们可以看到一个新角色出现在遊戲時会出现影响游戏平衡和玩家游戏体验的场景。
面向过程:就是程序按照步骤一个一个地去执行。优点在于极大得降低了写程序的复杂难度,只要顺着要执行的步骤堆叠代码就好;
缺点是一套流水线或者流程就是解决一个问题,代码牵一发而动全身。
面向函数:就是将编程分成很多种情况,把每一种情况写成一种函数,然后要按步骤执行函数完成。
类
class Cat:
def __init__(self,*args):
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
实例化一个对象再去查看它的内部属性(用法:对象名.属性名)
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
print(cat1.name) #jackey
print(cat1.height) #20
print(cat1.weight) #45
print(cat1.type) #cat
也可以用实例化名.__dict__[属性名]的方法查看属性
print(cat1.__dict__['name']) #jackey print(cat1.__dict__['height']) #20 print(cat1.__dict__['weight']) #45 print(cat1.__dict__['type']) #cat
利用self.__dict__查看属性
class Cat:
def __init__(self,*args):
print(self.__dict__) #{} 当我们还没加入属性时,self没有内容
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
print(self.__dict__) #{'name': 'jackey', 'height': 20, 'weight': 45, 'type': 'cat'}
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
print(cat1.__dict__) #{'name': 'jackey', 'height': 20, 'weight': 45, 'type': 'cat'}
__init__()中的参数self可以理解为一个字典,即一个对象,它把后面的属性name、height、weight等都存在了这个self'字典'中,再把每一个属性和它对应的值都交给我们实例化的对象
class Cat:
def __init__(self,*args):
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
print(self,id(self)) #<__main__.Cat object at 0x00000000025B1DD8> 39525848
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
print(cat1,id(cat1)) #<__main__.Cat object at 0x00000000025B1DD8> 39525848
可以从执行结果看出来它们共用的是同一个内存地址
查看一个类函数内部的静态属性(用法:类名.静态属性)和调用方法(用法:对象名.方法名()):
class Cat:
Rose = 'cat' #静态属性
def __init__(self,*args):
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
def Eat(self):
print('吃点什么...')
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
print(Cat.Rose) #cat
cat1.Eat() #吃点什么...
类名.__dict__
实例化名.__dict__它存储的是self里面的属性
而类名.__dict__它存储的是这个类中的所有名字
print(Cat.__dict__) #{'__module__': '__main__', '__init__': <function Cat.__init__ at 0x0000000002588950>, 'Eat': <function Cat.Eat at 0x0000000002588A60>, '__dict__': <attribute '__dict__' of 'Cat' objects>, '__weakref__': <attribute '__weakref__' of 'Cat' objects>, '__doc__': None}
print(cat1.__dict__) #'name': 'jackey', 'height': 20, 'weight': 45, 'type': 'cat'}
执行流程说明:
class Cat:
def __init__(self,*args):
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
def Eat(self):
print('{}正在吃小鱼干...'.format(self.name))
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
#这两种函数调用方法都是可以的
#Cat.Eat(cat1) #jackey正在吃小鱼干...
cat1.Eat() #jackey正在吃小鱼干...
1.首先先定义了一个Cat类
2.Cat() 即调用类名加括号和内容相当于执行了__init__()函数
3.在Cat()中的所有参数都会被*args以元祖的形式接收,而self是函数内部自己创建的一个类似于字典对象的一个东西
4.之后再执行赋值过程self.name = args[0]...执导所有属性赋值完成
5.这个时候self参数会把这个所有属性都传值给外面我们实例化出的对象cat1,注意:如果self里面是没有值的话,外面的实例化对象是没有内容的
6.最后通过类.函数名(实例化对象名字)或实例化对象名字.函数名()来调用函数,执行Eat方法,再打印结果

修改属性
print(cat1.name) #jackey
cat1.name = 'Maria'
print(cat1.name) #Maria
print(cat1.__dict__) #{'name': 'Maria', 'height': 20, 'weight': 45, 'type': 'cat'}
也可以用__dict__来修改
print(cat1.name) #jackey
cat1.__dict__['name'] = 'Maria'
print(cat1.name) #Maria
print(cat1.__dict__) #{'name': 'Maria', 'height': 20, 'weight': 45, 'type': 'cat'}
增加一个新的属性
class Cat:
def __init__(self,*args):
self.name = args[0]
self.height = args[1]
self.weight = args[2]
self.type = args[3]
def Eat(self):
print('{}正在吃小鱼干...'.format(self.name))
cat1 = Cat('jackey',20,45,'cat') #实例化一个对象
print(cat1.__dict__) #{'name': 'jackey', 'height': 20, 'weight': 45, 'type': 'cat'}
cat1.age = 5
print(cat1.age) #5
print(cat1.__dict__) #{'name': 'jackey', 'height': 20, 'weight': 45, 'type': 'cat', 'age': 5}
其他的类属性
类名.__name__ # 类的名字(字符串) 类名.__doc__ # 类的文档字符串 类名.__base__ # 类的第一个父类(在讲继承时会讲) 类名.__bases__ # 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__ # 类的字典属性 类名.__module__ # 类定义所在的模块 类名.__class__ # 实例对应的类(仅新式类中)
总结
什么是类? 具有相同特征的一类事物(人、狗、猫、飞机等)
什么是对象? 具体的某个事物(303房间的人、那支笔等)
什么是实例化? 类->对象的过程
python中一切皆为对象,类型的本质就是类
什么时候会用到面向对象呢?比如非常明显地处理一类事物,这类事物都具有相似的属性和功能;当有几个函数需要反复传入相同的参数时,就可以考虑面向对象;这些参数都是对象的属性
练习
用面向对象的方式表示一个圆的周长和面积,并求出它的周长和面积
from math import pi as P
class Circle:
def __init__(self,r):
self.r = r
def S(self):
return P*self.r**2
def L(self):
return 2*P*self.r
c1 = Circle(1)
print(c1.S()) #3.141592653589793
print(c1.L()) #6.283185307179586