面向对象
一、面向对像概念
面向对象概念的关键是对象。与面向过程不同,面向过程是针对问题,将解决问题的方式按步骤一步一步罗列。而面向对象则是,创建对象,然后对象之间交互。针对对象而不针对过程。那么什么是对象?对象是特征(静态属性)与技能(动态属性)的结合体。如游戏中的人物,有皮肤、姓名等属性,也有各种攻击技能等。
优点:解决了程序的扩展性。
缺点:1、复杂度提高;2、无法准确的预测处理流程与结果
二、类与对象
2.1类与对象的示例
class Dog: role='dog' counter=0 def __init__(self,name,age,kind,atc,hd): self.name=name self.age=age self.kind=kind self.hd=hd self.atc=atc def dog_func(self,pers): pers.hd-=self.atc print('%s bite %s ; %s hd lost %s still have %s '% (self.name,pers.name,pers.name,self.atc,pers.hd))# #注意:# 1.类中可以有任意python代码,这些代码在类定义阶段便会执行# 2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过Dog.__dict__查看# 3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法# 4.点是访问属性的语法,类中定义的名字,都是类的属性#程序中类的用法# .:专门用来访问属性,本质操作的就是__dict__Dog.role #等于经典类的操作Dog.__dict__['name']Dog.role='Oldboy' #等于经典类的操作Dog.__dict__['name']='Oldboy'Dog.counter=1 #等于经典类的操作Dog.__dict__['age']=1del Dog.counter #等于经典类的操作Dog.__dict__.pop('conuter')#程序中的对象#调用类,或称为实例化,得到对象# d1=Dog()# d2=Dog()# d3=Dog()#如此,d1、d2、d3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__#注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值d1=Dog('dog1',18,'二哈',1000,2000) #先调用类产生空对象s1,然后调用Dog.__init__('dog1',18,'二哈',1000,2000)d2=Dog('dog2',18,'金毛',800,2600)d3=Dog('dog3',18,'柯基',600,1800)#程序中对象的用法#执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间d2.__dict__{ 'name': 'dog2', 'age': 18, 'kind': '金毛', 'hd': 2600, 'atc': 800}d2.name #s2.__dict__['name']d2.name='dog222' #s2.__dict__['name']='王三炮'd2.course='python' #s2.__dict__['course']='python'del d2.course #s2.__dict__.pop('course')# 在程序中:先定义类,后产生对象
2.2python 中为类内置的特殊属性
#python为类内置的特殊属性类名.__name__# 类的名字(字符串)类名.__doc__# 类的文档字符串类名.__base__# 类的第一个父类(在讲继承时会讲)类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)类名.__dict__# 类的字典属性类名.__module__# 类定义所在的模块类名.__class__# 实例对应的类(仅新式类中)
2.3 对象(obj)查找属性的顺序
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
三、继承与派生
继承是一种创建新类的方式,python 中创建新类可以继承一个或多个父类,父类成为基类或超类,新建的类称为派生类或子类
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。
不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.
注意:像g1.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。
class ParentClass1: #定义父类 passclass ParentClass2: #定义父类 passclass SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass passclass SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
3.1查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类(,)>>> SubClass2.__bases__( , )
3.2经典类与新式类
1).只有在python2中才分新式类和经典类,python3中统一都是新式类
2).在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类 3).在python2中,显式地声明继承object的类,以及该类的子类,都是新式类 3).在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类 ps 如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__(,)>>> ParentClass2.__bases__( ,)
class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def move_forward(self): print('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivityclass Garen(Hero): passclass Riven(Hero): passg1=Garen('草丛伦',100,300)r1=Riven('锐雯雯',57,200)print(g1.life_value)r1.attack(g1)print(g1.life_value)'''运行结果'''
3.3 派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
class Riven(Hero): camp='Noxus' def __init__(self,nickname,aggressivity,life_value,skin): Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能 self.skin=skin #新属性 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 Hero.attack(self,enemy) #调用功能 print('from riven') def fly(self): #在自己这里定义新的 print('%s is flying' %self.nickname)r1=Riven('锐雯雯',57,200,'比基尼')r1.fly()print(r1.skin)'''运行结果锐雯雯 is flying比基尼'''
3.4 继承顺序
python中类能够继承多个类,则子类寻找方法(动态属性),的顺序有两种,分别为 广度优先和深度优先。
经典类遵循深度优先,新式类遵循广度优先
class A(object): def test(self): print('from A')class B(A): def test(self): print('from B')class C(A): def test(self): print('from C')class D(B): def test(self): print('from D')class E(C): def test(self): print('from E')class F(D,E): # def test(self): # print('from F') passf1=F()f1.test()print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性#新式类继承顺序:F->D->B->E->C->A#经典类继承顺序:F->D->B->A->E->C#python3中统一都是新式类#pyhon2中才分新式类与经典类
3.5 子类调用父类中的方法
###指名道姓法 #_*_coding:utf-8_*___author__ = 'Linhaifeng'class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...')class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) Vehicle.run(self)line13=Subway('中国地铁','180m/s','1000人/箱','电',13)line13.run()
####super()法class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...')class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run()class Mobike(Vehicle):#摩拜单车 passline13=Subway('中国地铁','180m/s','1000人/箱','电',13)line13.run()
#####指明道姓法与super()最好不要混用
了解部分:
即使没有直接继承关系,super仍然会按照mro继续往后查找
#A没有继承B,但是A内super会基于C.mro()继续往后找class A: def test(self): super().test()class B: def test(self): print('from B')class C(A,B): passc=C()c.test() #打印结果:from Bprint(C.mro())#[, , , ]
#指名道姓class A: def __init__(self): print('A的构造方法')class B(A): def __init__(self): print('B的构造方法') A.__init__(self)class C(A): def __init__(self): print('C的构造方法') A.__init__(self)class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) passf1=D() #A.__init__被重复调用'''D的构造方法B的构造方法A的构造方法C的构造方法A的构造方法'''#使用super()class A: def __init__(self): print('A的构造方法')class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__()class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__()class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__()f1=D() #super()会基于mro列表,往后找'''D的构造方法B的构造方法C的构造方法A的构造方法'''
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
四、组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
>>> class Equip: #武器装备类... def fire(self):... print('release Fire skill')... >>> class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类... camp='Noxus'... def __init__(self,nickname):... self.nickname=nickname... self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性... >>> r1=Riven('锐雯雯')>>> r1.equip.fire() #可以使用组合的类产生的对象所持有的方法release Fire skill