繼承
繼承指的是類與類之間的關系,是一種什么“是”什么的關系,繼承的功能之一就是用來解決代碼重用問題,
繼承是一種創建新類的方式,在python中新建的類可以繼承一個或多個父類,父類可稱為基類或者超類,新建的類稱為派生類或子類。
python中類的繼承分為:單繼承和多繼承
class Biology: # 定義一個父類 pass class Animal: # 定義一個父類 pass class Person(Biology): # 單繼承,基類是Biology, 派生類是person pass class Dog(Biology, Animal): # 多繼承,用逗號隔開多個基類 pass
查看繼承:
# 可通過類名.__bases__查看所有繼承的父類,類名.__base__只查看從左到右繼承的第一個父類 print(Dog.__base__) # <class '__main__.Biology'> print(Dog.__bases__) # (<class '__main__.Biology'>, <class '__main__.Animal'>)
經典類和新式類:
1.只有在python2中才分新式類和經典類,python3中統一都是新式類
2.在python2中,沒有聲明繼承object類的類,以及該類的子類,都是經典類
3.在python2中,聲明繼承object的類,以及該類的子類,都是新式類
4.在python3中,無論是否繼承object,都默認繼承object,即python3中所有類均為新式類
繼承與抽象:
抽象即抽取類似或比較像的部分。
抽象分為兩個層次:
1.把多個對象中比較像的部門抽取成類
2.把多個類中比較像的部門抽取成父類
抽象最主要的作用是划分類別(可以隔離關注點,降低復雜度)
繼承:是基於抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
繼承與重用性
class Hero(object): # 英雄的類 def __init__(self, name, camp, money, life_value, aggressivity, defensive): # 類的屬性包括:英雄的名字,陣營,資產,生命值,攻擊力,防御力 self.name = name self.camp = camp self.money = money self.life_value = life_value self.aggressivity = aggressivity self.defensive = defensive def attack(self, enemy): # 類的技能,英雄具有攻擊技能 enemy.life_value -= self.aggressivity class Garen(Hero): # Garen類繼承Hero類 pass class Riven(Hero): # Riven類繼承Hero類 pass garen1 = Garen("德瑪西亞之力", "德瑪西亞", 1000, 300, 60, 40) # 實例化Garen類時,Hero類中定義的屬性直接使用 riven1 = Riven("銳萌萌", "諾克薩斯", 1000, 280, 70, 30) garen1.attack(riven1) # Garen類實力話的對象可以直接使用Hero類中定義的方法 print(garen1.life_value) print(riven1.life_value)
通過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的所有屬性(數據屬性和函數屬性),實現代碼重用。
派生
class Garen(Hero): # Garen類繼承Hero類 def attack(self, enemy): # 子類可以重新定義攻擊技能,不會改變父類的攻擊方法 enemy.life_value -= (self.aggressivity - enemy.defensive) def life_reply(self): # 子類可以新加一個生命恢復技能 self.life_value += 20
屬性查找順序
先從自己內部找,然后再去類里找,最后再去父類中找。
父類中查找的順序
經典類:按繼承的類的順序從左到右深度優先
新式類:按繼承的類的順序從左到右廣度優先
對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構造是通過一個C3線性化算法來實現的。
class A(object): def test(self): print("from a") class B(A): pass # def test(self): # print("from b") class C(A): pass # def test(self): # print("from c") class D(B): pass # def test(self): # print("from d") class E(C): pass # def test(self): # print("from e") class F(A): pass # def test(self): # print("from f") class H(D,E,F): pass h1 = H() h1.test() # from a print(H.__mro__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 # (<class '__main__.H'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.A'>, <class 'object'>)
子類里調用父類的方法
在子類派生出的新方法中,往往需要重用父類的方法,我們有兩種方式實現
一、指名道姓,即父類名.父類方法()
二、super()
class Hero(object): # 英雄的類 def __init__(self, name, camp, money, life_value, aggressivity, defensive): # 類的屬性包括:英雄的名字,陣營,資產,生命值,攻擊力,防御力 self.name = name self.camp = camp self.money = money self.life_value = life_value self.aggressivity = aggressivity self.defensive = defensive def attack(self, enemy): # 類的技能,英雄具有攻擊技能 enemy.life_value -= self.aggressivity class Garen(Hero): # Garen類繼承Hero類 def __init__(self, name, camp, money, life_value, aggressivity, defensive, sex): # Hero.__init__(self, name, camp, money, life_value, aggressivity, defensive) # 指名道姓 # super(Garen, self).__init__(name, camp, money, life_value, aggressivity, defensive) # python2 super().__init__(name, camp, money, life_value, aggressivity, defensive) # python3中 super() = super(Garen, self) self.sex = sex def attack(self, enemy): # 子類可以重新定義攻擊技能,不會改變父類的攻擊方法 enemy.life_value -= (self.aggressivity - enemy.defensive) def life_reply(self): # 子類可以新加一個生命恢復技能 self.life_value += 20 class Riven(Hero): # Riven類繼承Hero類 pass garen1 = Garen("德瑪西亞之力", "德瑪西亞", 1000, 300, 60, 40, "man") riven1 = Riven("銳萌萌", "諾克薩斯", 1000, 280, 70, 30) garen1.attack(riven1) # Garen類實力話的對象可以直接使用Hero類中定義的方法 print(garen1.life_value) print(riven1.life_value)
兩種方式的區別:方式一時跟繼承沒有關系的,而方式二的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): pass c=C() c.test() #打印結果:from B print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
組合
組合指的是類與類之間的關系,是一種什么“有”什么的關系,在一個類中以另一個類的對象作為數據屬性,稱為類的組合
class Arms: # 定義一個武器類 def __init__(self, aggressivity): # 定義武器攻擊力屬性 self.aggressivity = aggressivity class Hero: # 定義一個英雄的類 role = "hero" # 定義一個公同屬性角色 def __init__(self, camp, life_value, aggressivity, defensive, arm=0): # 定義一個自定義值的屬性函數 屬性包括生命值,攻擊力,防御,和武器屬性,默認是0,傳入武器后改變 self.camp = camp self.life_value = life_value self.aggressivity = aggressivity self.defensive = defensive self.arm = [] # 裝備的武器列表 def attack(self, target): # 攻擊技能 aggressvity = self.aggressivity if self.arm: # 判斷是否裝備了武器 for i in self.arm: aggressvity += i.aggressivity # 將武器的攻擊力加上 target.life_value -= (aggressvity - target.defensive) garen = Hero("Demarcia", 100, 60, 30) # 實例化一個蓋倫對象,並傳入英雄的生命值,攻擊,防御,武器 rivan = Hero("xx", 90, 70, 20) # 實例化一個銳雯對象,並傳入英雄的生命值,攻擊,防御,武器 big_sword = Arms(60) # 實例化武器的類,生成一個名叫大劍的武器 garen.arm.append(big_sword) # 給蓋倫裝備裝大劍 garen.attack(rivan) # 蓋倫攻擊銳雯一次 print(rivan.life_value)
總結:
當類之間有顯著不同,並且較小的類是較大的類所需要的組件時,用組合比較好