Python学习日记(二十四) 继承
继承
什么是继承?就是一个派生类(derived class)继承基类(base class)的字段和方法。一个类可以被多个类继承;在python中,一个类可以继承多个类。
父类可以称为基类和超类,而子类可以称为派生类
在继承中可分为单继承和多继承两种
下面是继承的用法,语法为'class 子类的名字(父类名):'
class plane: #定义一个所有战机的父类 def __init__(self,name,speed,hp,atk): self.name = name self.speed = speed self.hp = hp self.atk = atk class fighter(plane): #定义一个fighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,money): self.name = name self.speed = speed self.hp = hp self.atk = atk self.money= money def attack(self,enemyfighter): enemyfighter.hp -= self.atk class enemyfighter(plane): #定义一个enemyfighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,type): self.name = name self.speed = speed self.hp = hp self.atk = atk self.type = type def enemyattack(self,fighter): fighter.hp -= self.atk
我们如果想知道一个类的父类是谁,可以使用__bases__方法查看
print(plane.__bases__) #(<class 'object'>,) print(fighter.__bases__) #(<class '__main__.plane'>,) print(enemyfighter.__bases__) #(<class '__main__.plane'>,)
可以从结果看出两个子类都继承了plane这个父类,而plane类它继承的是类的'祖宗'object类。在一个python3里所有的类都有父类,如果一个类它没有发生继承那么它的父类就是object的子类。
新式类:没有继承父类默认继承object类
抽象的概念
抽象就是抽取类似或比较像的部分
分为两个层次:将两个比较相似的对象比较像的部分抽取成类和把多个类比较像的部分抽取成父类
继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式表达出抽象的结构;且类与类之间才有继承的关系
单继承
我们在写上面的代码时候可以发现fighter类和enemyfighter类中有很多属性在父类都是重复的,并且有些属性又是自己特有的,那么对于这个派生类特有的属性我们称为派生属性。下面我们修改我们上面的代码:
class plane: #定义一个所有战机的父类 def __init__(self,name,speed,hp,atk): self.name = name self.speed = speed self.hp = hp self.atk = atk class fighter(plane): #定义一个fighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,money): plane.__init__(self,name,speed,hp,atk) #这里的self是fighter的self self.money= money #派生属性 def attack(self,enemyfighter): enemyfighter.hp -= self.atk class enemyfighter(plane): #定义一个enemyfighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,type): plane.__init__(self,name,speed,hp,atk) #这里的self是enemyfighter的self self.type = type #派生属性 def enemyattack(self,fighter): fighter.hp -= self.atk f1 = fighter('player1',150,1000,100,500) print(f1.__dict__) #{'name': 'player1', 'speed': 150, 'hp': 1000, 'atk': 100, 'money': 500} boss1 = enemyfighter('aks-89',50,3000,500,'boss') print(boss1.__dict__) #{'name': 'aks-89', 'speed': 50, 'hp': 3000, 'atk': 500, 'type': 'boss'}
现在给plane类添加一个方法attack,当如果子类和父类的方法重名时,在子类在调用的时候,如果子类中有这个名字那么就一定是用子类的,子类没有才找父类的,如果父类没有就报错
class plane: #定义一个所有战机的父类 def __init__(self,name,speed,hp,atk): self.name = name self.speed = speed self.hp = hp self.atk = atk def attack(self): print(self.name+'发射子弹!') class fighter(plane): #定义一个fighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,money): plane.__init__(self,name,speed,hp,atk) #这里的self是fighter的self self.money= money #派生属性 def attack(self,enemyfighter): enemyfighter.hp -= self.atk print('now {0} hp : {1}'.format(enemyfighter.name,enemyfighter.hp)) class enemyfighter(plane): #定义一个enemyfighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,type): plane.__init__(self,name,speed,hp,atk) #这里的self是enemyfighter的self self.type = type #派生属性 def enemyattack(self,fighter): fighter.hp -= self.atk print('now {0} hp : {1}'.format(fighter.name, fighter.hp)) f1 = fighter('player1',150,1000,100,500) boss1 = enemyfighter('aks-89',50,3000,500,'boss') f1.attack(boss1) #now aks-89 hp : 2900 boss1.enemyattack(f1) #now player1 hp : 500 boss1.attack() #aks-89发射子弹!
派生方法:父类中没有但在子类中特有的方法,例如上面的enemyattack()
如果一个子类还想用父类的东西,应该单独调用父类的
<1>父类名.类方法名(self参数),这里的self参数必须传
class fighter(plane): #定义一个fighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,money): plane.__init__(self,name,speed,hp,atk) #这里的self是fighter的self self.money= money #派生属性 def attack(self,enemyfighter): plane.attack(self) #如果既想实现新的功能也想使用父类原本的功能,还需要在子类中调用父类 enemyfighter.hp -= self.atk print('now {0} hp : {1}'.format(enemyfighter.name,enemyfighter.hp))
f1 = fighter('player1',150,1000,100,500) boss1 = enemyfighter('aks-89',50,3000,500,'boss') f1.attack(boss1) #player1发射子弹! #now aks-89 hp : 2900
<2>super方法
class plane: #定义一个所有战机的父类 def __init__(self,name,speed,hp,atk): self.name = name self.speed = speed self.hp = hp self.atk = atk def attack(self): print(self.name+'发射子弹!') class fighter(plane): #定义一个fighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,money): super().__init__(name,speed,hp,atk) #这里的self是fighter的self self.money= money #派生属性 def attack(self,enemyfighter): plane.attack(self) #如果既想实现新的功能也想使用父类原本的功能,还需要在子类中调用父类 enemyfighter.hp -= self.atk print('now {0} hp : {1}'.format(enemyfighter.name,enemyfighter.hp)) class enemyfighter(plane): #定义一个enemyfighter类 它继承的是plane类 def __init__(self,name,speed,hp,atk,type): super().__init__(name,speed,hp,atk) #这里的self是enemyfighter的self self.type = type #派生属性 def enemyattack(self,fighter): plane.attack(self) fighter.hp -= self.atk print('now {0} hp : {1}'.format(fighter.name, fighter.hp)) f1 = fighter('player1',150,1000,100,500) boss1 = enemyfighter('aks-89',50,3000,500,'boss') f1.attack(boss1) #player1发射子弹! #now aks-89 hp : 2900 boss1.enemyattack(f1) #aks-89发射子弹! #now player1 hp : 500
super()函数在这里省略了两个参数,分别是子类名和self参数。super()只在新式类有并且它只在python3存在,而在python3中所有的类都是新式类。对于单继承来说super()就可以找到他的父类了;上面的super()用法是在类的内部使用。
super()在类的外部使用:
f1 = fighter('player1',150,1000,100,500) boss1 = enemyfighter('aks-89',50,3000,500,'boss') super(fighter,f1).attack() #player1发射子弹! super(enemyfighter,boss1).attack() #aks-89发射子弹!
可以直接找父类的这一个函数并进行调用
在单继承中,一个类它只继承一个基类且一般来说它能够减少代码的重复,提高代码可读性,规范编程模式
多继承
多继承顾名思义就是一个类它继承了两个或两个以上的父类
<1>钻石继承:
假设有4个类它们的继承关系如下图表示
class a: def fuc(self): print('a') class c(a): def fuc(self): print('c') class d(a): def fuc(self): print('d') class b(c,d): def fuc(self): print('b') b = b() b.fuc() #b
如果把b类的方法注释掉现在的结果是什么?
class a: def fuc(self): print('a') class c(a): def fuc(self): print('c') class d(a): def fuc(self): print('d') class b(c,d): pass # def fuc(self): # print('b') b = b() b.fuc() #c
再注释掉c类的方法
class a: def fuc(self): print('a') class c(a): pass # def fuc(self): # print('c') class d(a): def fuc(self): print('d') class b(c,d): pass # def fuc(self): # print('b') b = b() b.fuc() #d
所以在最后一次执行的结果就是a了
我们也可以通过b.mro()的方法来知道python是怎么走的
class a: def fuc(self): print('a') class c(a): def fuc(self): print('c') class d(a): def fuc(self): print('d') class b(c,d): def fuc(self): print('b') b = b() print(b.mro()) #[<class '__main__.b'>, <class '__main__.c'>, <class '__main__.d'>, <class '__main__.a'>, <class 'object'>]
在这里为什么先找的是d而不是a呢?虽然python在找的时候它其实已经知道了c后面有一个a,但是它要优先遵循从左往右的方向去找并且c->a,d->a,如果它直接找到a的话那么d的节点就会丢失,如果一个节点丢失的话就再也找不回来了,所以第三次结果它打印了d。
<2>乌龟继承:
这些类的继承关系如下图表示
class a: def fuc(self): print('a') class b(a): def fuc(self): print('b') class f(a): def fuc(self): print('f') class c(b): def fuc(self): print('c') class e(f): def fuc(self): print('e') class d(e,c): def fuc(self): print('d') print(d.mro())
#[<class '__main__.d'>, <class '__main__.e'>, <class '__main__.f'>, <class '__main__.c'>, <class '__main__.b'>, <class '__main__.a'>, <class 'object'>]
这种记录继承顺序它是新式类的继承顺序,所遵循的是广度优先
而在python2.7中就是经典类,它所遵循的深度优先即走过的路就不走了,在这里的结果就是d->e->f->a->c->b
总结:
如果是多个父类中有一个方法的名字都相同,一个子类继承了这些父类,当它去用这个方法的时候,它会优先从左往右去找
python2.7 新式类和经典类共存,新式类要继承object
python3 只有新式类,默认继承object
经典类和新式类还有一个区别就是mro方法之在新式类存在
super的本质
用到上面钻石继承的继承关系图,但代码稍微改动
class a: def fuc(self): print('a') class c(a): def fuc(self): super().fuc() print('c') class d(a): def fuc(self): super().fuc() print('d') class b(c,d): def fuc(self): super().fuc() print('b') b = b() b.fuc() # a # d # c # b
super它的本质不是单纯找父类,而是根据调用者的节点位置的广度优先顺序来找的
具体执行流程: