python-繼承與重用
什麽是繼承?
繼承是一種創建新類的方式,新建的類可以繼承一個或多個父類(python支持多繼承),父類又可稱為基類或超類,新建的類稱為派生類或子類。子類會“”遺傳”父類的屬性,從而解決代碼重用問題(比如練習7中Garen與Riven類有很多冗余的代碼)
單繼承與多繼承
class ParentClass_one: #定義父類 pass class ParentClass_two: #定義父類 pass class SubClass_one(ParentClass_one): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass_two(ParentClass_one,ParentClass_two): #python支持多繼承,用逗號分隔開多個繼承的類 pass
經典類與新式類
- 只有在python2中才分新式類和經典類,python3中統一都是新式類
- 在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類
- 在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類
- 在python3中,無論是否繼承object,都默認繼承object,即python3中所有類均為新式類
繼承與抽象
- 繼承描述的是子類與父類之間的關系,是一種什麽是什麽的關系。要找出這種關系,必須先抽象再繼承,抽象即抽取類似或者說比較像的部分。抽象最主要的作用是劃分類別(可以隔離關註點,降低復雜度)
- 繼承是基於抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
繼承與重用性
在開發程序的過程中,如果我們定義了一個類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 g=Garen(‘草叢倫‘,100,300) r=Riven(‘銳雯雯‘,57,200) print(g.life_value) r1.attack(g) print(g.life_value) ‘‘‘ 運行結果 243 ‘‘‘ ps:像g.life_value之類的屬性引用,會先從實例中找life_value然後去類中找,然後再去父類中找...直到最頂級的父類。
派生
子類也可以添加自己新的屬性或者在自己這裏重新定義這些屬性(不會影響到父類),需要一旦重新定義了自己的屬性且與父類重名,那麽調用新增的屬性時,就以自己為準。
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) r=Riven(‘銳雯雯‘,57,200,‘比基尼‘) r.fly() print(r.skin) ‘‘‘ 運行結果 銳雯雯 is flying 比基尼 ‘‘‘
組合與重用性
組合指的是,在一個類中以另外一個類的對象作為數據屬性,稱為類的組合
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屬性 r=Riven(‘銳雯雯‘) print(r.equip.fire()) #可以使用組合的類產生的對象所持有的方法 ‘‘‘ release Fire skill ‘‘‘
組合與繼承
- 繼承的方式:通過繼承建立了派生類與基類之間的關系,它是一種‘是‘的關系,比如白馬是馬,人是動物。當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學生是人
- 組合的方式:用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python和linux課程,教授有學生s1、s2、s3...
ps:當類之間有顯著不同,並且較小的類是較大的類所需要的組件時,使用組合比較好
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Course: def __init__(self,name,period,price): self.name=name self.period=period self.price=price def tell_info(self): print(‘<%s %s %s>‘ %(self.name,self.period,self.price)) class Teacher(People): def __init__(self,name,age,sex,job_title): People.__init__(self,name,age,sex) self.job_title=job_title self.course=[] self.students=[] class Student(People): def __init__(self,name,age,sex): People.__init__(self,name,age,sex) self.course=[] jack=Teacher(‘jack‘,18,‘male‘,‘computer teacher‘) s=Student(‘tom‘,18,‘female‘) python=Course(‘python‘,‘3mons‘,3000.0) linux=Course(‘python‘,‘3mons‘,3000.0) #為老師jack和學生s1添加課程 jack.course.append(python) jack.course.append(linux) s.course.append(python) #為老師jack添加學生s jack.students.append(s) #使用 for obj in jack.course: obj.tell_info()
繼承實現原理
在Java和C#中子類只能繼承一個父類,而Python中子類可以同時繼承多個父類,如A(B,C,D),如果繼承關系為非菱形結構,則會按照先找B這一條分支,然後再找C這一條分支,最後找D這一條分支的順序直到找到我們想要的屬性,如果繼承關系為菱形結構,那麽屬性的查找方式有兩種,分別是:深度優先和廣度優先
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‘) pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
Python實現繼承
python是如何實現繼承的,對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,比如:F.mro() #等同於F.__mro__:[<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>],為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構造是通過一個C3線性化算法來實現的。我們不去深究這個算法的數學原理,它實際上就是合並所有父類的MRO列表並遵循如下三條準則:
- 子類會先於父類被檢查
- 多個父類會根據它們在列表中的順序被檢查
- 如果對下一個類存在兩個合法的選擇,選擇第一個父類
子類調用父類的方法
方法一:指名道姓,即父類名.父類方法()
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):#摩拜單車 pass line13=Subway(‘中國地鐵‘,‘180m/s‘,‘1000人/箱‘,‘電‘,13) line13.run()
python-繼承與重用