最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

9.3 繼承和多態(tài)

2021-09-22 08:18 作者:海鷗之道  | 我要投稿

在OOP程序設(shè)計中,當我們定義一個class的時候,可以從某個現(xiàn)有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。

比如,我們已經(jīng)編寫了一個名為Animal的class,有一個run()方法可以直接打印:

當我們需要編寫DogCat類時,就可以直接從Animal類繼承:

對于Dog來說,Animal就是它的父類,對于Animal來說,Dog就是它的子類。CatDog類似。

繼承有什么好處?最大的好處是子類獲得了父類的全部功能。由于Animial實現(xiàn)了run()方法,因此,DogCat作為它的子類,什么事也沒干,就自動擁有了run()方法:

運行結(jié)果如下:

當然,也可以對子類增加一些方法,比如Dog類:

繼承的第二個好處需要我們對代碼做一點改進。你看到了,無論是Dog還是Cat,它們run()的時候,顯示的都是Animal is running...,符合邏輯的做法是分別顯示Dog is running...Cat is running...,因此,對DogCat類改進如下:

再次運行,結(jié)果如下:

當子類和父類都存在相同的run()方法時,我們說,子類的run()覆蓋了父類的run(),在代碼運行的時候,總是會調(diào)用子類的run()。這樣,我們就獲得了繼承的另一個好處:多態(tài)。

要理解什么是多態(tài),我們首先要對數(shù)據(jù)類型再作一點說明。當我們定義一個class的時候,我們實際上就定義了一種數(shù)據(jù)類型。我們定義的數(shù)據(jù)類型和Python自帶的數(shù)據(jù)類型,比如str、list、dict沒什么兩樣:

判斷一個變量是否是某個類型可以用isinstance()判斷:

看來a、b、c確實對應著list、Animal、Dog這3種類型。

但是等等,試試:

看來c不僅僅是Dogc還是Animal!

不過仔細想想,這是有道理的,因為Dog是從Animal繼承下來的,當我們創(chuàng)建了一個Dog的實例c時,我們認為c的數(shù)據(jù)類型是Dog沒錯,但c同時也是Animal也沒錯,Dog本來就是Animal的一種!

所以,在繼承關(guān)系中,如果一個實例的數(shù)據(jù)類型是某個子類,那它的數(shù)據(jù)類型也可以被看做是父類。但是,反過來就不行:

Dog可以看成Animal,但Animal不可以看成Dog。

要理解多態(tài)的好處,我們還需要再編寫一個函數(shù),這個函數(shù)接受一個Animal類型的變量:

當我們傳入Animal的實例時,run_twice()就打印出:

當我們傳入Dog的實例時,run_twice()就打印出:

當我們傳入Cat的實例時,run_twice()就打印出:

看上去沒啥意思,但是仔細想想,現(xiàn)在,如果我們再定義一個Tortoise類型,也從Animal派生:

當我們調(diào)用run_twice()時,傳入Tortoise的實例:

你會發(fā)現(xiàn),新增一個Animal的子類,不必對run_twice()做任何修改,實際上,任何依賴Animal作為參數(shù)的函數(shù)或者方法都可以不加修改地正常運行,原因就在于多態(tài)。

多態(tài)的好處就是,當我們需要傳入Dog、Cat、Tortoise……時,我們只需要接收Animal類型就可以了,因為Dog、CatTortoise……都是Animal類型,然后,按照Animal類型進行操作即可。由于Animal類型有run()方法,因此,傳入的任意類型,只要是Animal類或者子類,就會自動調(diào)用實際類型的run()方法,這就是多態(tài)的意思:

對于一個變量,我們只需要知道它是Animal類型,無需確切地知道它的子類型,就可以放心地調(diào)用run()方法,而具體調(diào)用的run()方法是作用在Animal、Dog、Cat還是Tortoise對象上,由運行時該對象的確切類型決定,這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用,不管細節(jié),而當我們新增一種Animal的子類時,只要確保run()方法編寫正確,不用管原來的代碼是如何調(diào)用的。這就是著名的“開閉”原則:

對擴展開放:允許新增Animal子類;

對修改封閉:不需要修改依賴Animal類型的run_twice()等函數(shù)。

繼承還可以一級一級地繼承下來,就好比從爺爺?shù)桨职?、再到兒子這樣的關(guān)系。而任何類,最終都可以追溯到根類object,這些繼承關(guān)系看上去就像一顆倒著的樹。比如如下的繼承樹:

靜態(tài)語言 vs 動態(tài)語言

對于靜態(tài)語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調(diào)用run()方法。

對于Python這樣的動態(tài)語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了:

class Timer(object): ? ?def run(self): ? ? ? ?print('Start...')

這就是動態(tài)語言的“鴨子類型”,它并不要求嚴格的繼承體系,一個對象只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。

Python的“file-like object“就是一種鴨子類型。對真正的文件對象,它有一個read()方法,返回其內(nèi)容。但是,許多對象,只要有read()方法,都被視為“file-like object“。許多函數(shù)接收的參數(shù)就是“file-like object“,你不一定要傳入真正的文件對象,完全可以傳入任何實現(xiàn)了read()方法的對象。

小結(jié)

繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫。

動態(tài)語言的鴨子類型特點決定了繼承不像靜態(tài)語言那樣是必須的。






9.3 繼承和多態(tài)的評論 (共 條)

分享到微博請遵守國家法律
秭归县| 金乡县| 葫芦岛市| 浙江省| 环江| 山阳县| 博白县| 陆川县| 望奎县| 亳州市| 剑川县| 淮南市| 商都县| 德保县| 乌审旗| 青神县| 红桥区| 朔州市| 古田县| 祁东县| 沙坪坝区| 娱乐| 昔阳县| 大丰市| 凤台县| 长顺县| 祁连县| 富锦市| 婺源县| 南召县| 宁远县| 千阳县| 楚雄市| 平安县| 平陆县| 河北区| 万荣县| 黄大仙区| 海兴县| 天津市| 子长县|