繼承和多態(tài):
在OOP程序設(shè)計(jì)中,當(dāng)我們定義一個(gè)class的時(shí)候脚翘,可以從某個(gè)現(xiàn)有的class繼承灼卢,新的class稱為子類(Subclass),而被繼承的class稱為基類来农、父類或超類(Base class鞋真、Super class)。
假設(shè)有個(gè)父類是Animal,它有run()的方法沃于,
再設(shè)立兩個(gè)子類Dog(Animal)涩咖,Cat(Animal),繼承了父類海诲,這兩個(gè)子類自動(dòng)就擁有了run()方法,就算這兩個(gè)子類沒(méi)有定義run()方法檩互。
繼承有什么好處特幔?最大的好處是子類獲得了父類的全部功能。由于Animal實(shí)現(xiàn)了run()方法闸昨,因此敬辣,Dog和Cat作為它的子類,什么事也沒(méi)干零院,就自動(dòng)擁有了run()方法溉跃。
tips:判斷一個(gè)變量是否是某個(gè)類型可以用isinstance()判斷,提到很多次,應(yīng)該是忘不掉了告抄。
在繼承關(guān)系中撰茎,如果一個(gè)實(shí)例的數(shù)據(jù)類型是某個(gè)子類,那它的數(shù)據(jù)類型也可以被看做是父類打洼。但是龄糊,反過(guò)來(lái)就不行:Dog可以看成Animal,但Animal不可以看成Dog募疮。
多態(tài)的好處:
多態(tài)的好處就是炫惩,當(dāng)我們需要傳入Dog、Cat阿浓、Tortoise……時(shí)他嚷,我們只需要接收Animal類型就可以了,因?yàn)镈og芭毙、Cat筋蓖、Tortoise……都是Animal類型,然后退敦,按照Animal類型進(jìn)行操作即可粘咖。由于Animal類型有run()方法,因此侈百,傳入的任意類型瓮下,只要是Animal類或者子類,就會(huì)自動(dòng)調(diào)用實(shí)際類型的run()方法钝域,這就是多態(tài)的意思:
對(duì)于一個(gè)變量讽坏,我們只需要知道它是Animal類型,無(wú)需確切地知道它的子類型网梢,就可以放心地調(diào)用run()方法震缭,而具體調(diào)用的run()方法是作用在Animal、Dog战虏、Cat還是Tortoise對(duì)象上拣宰,由運(yùn)行時(shí)該對(duì)象的確切類型決定党涕,這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用,不管細(xì)節(jié)巡社,而當(dāng)我們新增一種Animal的子類時(shí)膛堤,只要確保run()方法編寫(xiě)正確,不用管原來(lái)的代碼是如何調(diào)用的晌该。這就是著名的“開(kāi)閉”原則:
對(duì)擴(kuò)展開(kāi)放:允許新增Animal子類肥荔;
對(duì)修改封閉:不需要修改依賴Animal類型的run_twice()等函數(shù)。
類的樹(shù)形圖:子類可以繼承父類
動(dòng)態(tài)語(yǔ)言(鴨子類型)與靜態(tài)語(yǔ)言的區(qū)別:
對(duì)于靜態(tài)語(yǔ)言(例如Java)來(lái)說(shuō)朝群,如果需要傳入Animal類型燕耿,則傳入的對(duì)象必須是Animal類型或者它的子類,否則姜胖,將無(wú)法調(diào)用run()方法誉帅。
對(duì)于Python這樣的動(dòng)態(tài)語(yǔ)言來(lái)說(shuō),則不一定需要傳入Animal類型右莱。我們只需要保證傳入的對(duì)象有一個(gè)run()方法就可以了蚜锨。
class Timer(object):
def run(self):
print('Start...')
沒(méi)有繼承Animal類,但是也能調(diào)用
這就是動(dòng)態(tài)語(yǔ)言的“鴨子類型”慢蜓,它并不要求嚴(yán)格的繼承體系亚再,一個(gè)對(duì)象只要“看起來(lái)像鴨子,走起路來(lái)像鴨子”晨抡,那它就可以被看做是鴨子氛悬。(還是不太理解)
自己的例子:
創(chuàng)建一個(gè)Animal類:
>>> class Animal(object):
...? ? def run(self):
...? ? ? ? ? ? print 'Animal is running...'
...? ? def run_twice(self):
...? ? ? ? ? ? self.run()
...? ? ? ? ? ? self.run()
...
創(chuàng)建一個(gè)Timer類
>>> class Timer(object):
...? ? def run(self):
...? ? ? ? ? ? print('Start...')
...
創(chuàng)建一個(gè)Cat類(繼承Animal)
>>> class Cat(Animal):
...? ? pass
...
創(chuàng)建一個(gè)Fuck類(繼承Timer)
>>> class Fuck(Timer):
...? ? pass
...
實(shí)例化對(duì)象:f = Fuck(),c = Cat()
>>> f.run()
Start...
>>> c.run()
Animal is running...
>>>
在我看來(lái)凄诞,run()這個(gè)方法在兩個(gè)父類都有圆雁,但在兩個(gè)子類中,run()這個(gè)方法起到的效果卻截然不通帆谍,兩個(gè)run()看起來(lái)很像,但是卻不相同轴咱。就像100美元和100RMB汛蝙,兩張面值都是100,但是能買(mǎi)到的東西卻不是等價(jià)的
Python的“file-like object“就是一種鴨子類型。對(duì)真正的文件對(duì)象朴肺,它有一個(gè)read()方法窖剑,返回其內(nèi)容。但是戈稿,許多對(duì)象西土,只要有read()方法,都被視為“file-like object“鞍盗。許多函數(shù)接收的參數(shù)就是“file-like object“需了,你不一定要傳入真正的文件對(duì)象跳昼,完全可以傳入任何實(shí)現(xiàn)了read()方法的對(duì)象。(自己對(duì)鴨子的用法和意義還是有待研究)
人家的評(píng)論肋乍,自己借鑒一下:
返回結(jié)果I am in 上海
I'm in 上海-浦東
我不屬于Province類或其子類鹅颊,但我有ps方法我同樣可以被調(diào)用
我不屬于Province類火氣子類,但我有ps1方法我同樣可以被調(diào)用
好像意思是墓造,A和B兩個(gè)類都有c方法堪伍,當(dāng)c方法傳入A類作為參數(shù),c方法的作用就是A類的c方法的作用觅闽;當(dāng)c方法傳入B類作為參數(shù)帝雇,c方法的作用就是B類的c方法的作用。
小結(jié)
繼承可以把父類的所有功能都直接拿過(guò)來(lái)蛉拙,這樣就不必重零做起摊求,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫(xiě)刘离。
動(dòng)態(tài)語(yǔ)言的鴨子類型特點(diǎn)決定了繼承不像靜態(tài)語(yǔ)言那樣是必須的室叉。