繼承 什麼是繼承?就是一個派生類(derived class)繼承基類(base class)的欄位和方法。一個類可以被多個類繼承;在python中,一個類可以繼承多個類。 父類可以稱為基類和超類,而子類可以稱為派生類 在繼承中可分為單繼承和多繼承兩種 下麵是繼承的用法,語法為'class 子類的名 ...
繼承
什麼是繼承?就是一個派生類(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第一次執行fuc
如果把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第二次執行fuc
再註釋掉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第三次執行fuc
所以在最後一次執行的結果就是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它的本質不是單純找父類,而是根據調用者的節點位置的廣度優先順序來找的
具體執行流程: