面向物件【林老師版】:繼承與派生(六)
一、初識繼承
1、什麼是繼承
繼承指的是類與類之間的關係,是一種什麼“是”什麼的關係,繼承的功能之一就是用來解決程式碼重用問題
繼承是一種建立新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可以成為基類或超類,新建的類稱為派生類或子類
2、python中類的繼承分為:單繼承和多繼承
class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支援多繼承,用逗號分隔開多個繼承的類 pass
3、檢視繼承
>>> SubClass1.__bases__ #__base__只檢視從左到右繼承的第一個子類,__bases__則是檢視所有繼承的父類 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
4、經典類與新式類(關於新式類與經典類的區別,我們稍後討論)
1.只有在python2中才分新式類和經典類,python3中統一都是新式類 2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類 3.在python2中,顯式地宣告繼承object的類,以及該類的子類,都是新式類 4.在python3中,無論是否繼承object,都預設繼承object,即python3中所有類均為新式類
提示:如果沒有指定基類,python的類會預設繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
二、繼承與抽象(先抽象再繼承)
抽象即抽取類似或者說比較像的部分。
抽象分成兩個層次:
1.將奧巴馬和梅西這倆物件比較像的部分抽取成類;
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關注點,降低複雜度)
繼承:是基於抽象的結果,通過程式語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
三、繼承與重用性
在開發程式的過程中,如果我們定義了一個類A,然後又想新建立另外一個類B,但是類B的大部分內容與類A的相同時
我們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
通過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的所有屬性(資料屬性和函式屬性),實現程式碼重用
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.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草叢倫',100,300) r1=Riven('銳雯雯',57,200) print(g1.life_value) #結果:300 r1.attack(g1) print(g1.life_value) #結果:243
提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟體中的一部分設定大部分,大大節省了程式設計工作量,這就是常說的軟體重用,不僅可以重用自己的類,也可以繼承別人的,比如標準庫,來定製新的資料型別,這樣就是大大縮短了軟體開發週期,對大型軟體開發來說,意義重大.
四、再看屬性查詢
提示:像g1.life_value之類的屬性引用,會先從例項中找life_value然後去類中找,然後再去父類中找...直到最頂級的父類。那麼如何解釋下面的列印結果呢?
class Foo: def f1(self): print('Foo.f1') def f2(self): print('Foo.f2') self.f1() class Bar(Foo): def f1(self): print('Bar.f1') b=Bar() b.f2()
列印結果
Foo.f2 Bar.f1
五、派生
當然子類也可以新增自己新的屬性或者在自己這裡重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那麼呼叫新增的屬性時,就以自己為準了。
class Riven(Hero): camp='Noxus' def attack(self,enemy): #在自己這裡定義新的attack,不再使用父類的attack,且不會影響父類 print('from riven') def fly(self): #在自己這裡定義新的 print('%s is flying' %self.nickname)
在子類中,新建的重名的函式屬性,在編輯函式內功能的時候,有可能需要重用父類中重名的那個函式功能,應該是用呼叫普通函式的方式,即:類名.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 比基尼