寫在前面的話
終於,又到了周五。當小伙伴們都不再加班歡歡喜喜過周末的時候,我剛剛寫完這一周的游戲作業,從面對晚歸的緊皺眉頭到現在的從容淡定,好像只有那么幾周的時間。突然發現:改變——原來這么簡單。很多時候我們在想要為自己的青春拼搏一次的時候都輸給了偶爾的抵觸和輕松愉悅的生活。我們不過和走到最后的人差了兩個字——堅持!所以盡管進入類和對象這一部分,大家都會有畏難心理,但是堅持一下下,你就會是那個走到最后的人!
回顧
上一篇中我們初步的認識了類和對象,了結了它們的一些語法,就相當於得到了一個對象的骨骼。今天再來補充一些和他們相關的內容,給我們的小骷髏添上肌肉和皮膚,它就是一個活生生的對象了,從此你也是一個有對象的人了!哈~
首先我們從面向對象的特性這個角度先回顧一下上一篇的內容,之前我們已經知道了“車”是一個類,車的“品牌”和“價格”是它的屬性,可以在路上跑是他的行為;因此我們定義了一個類car,並在這個car類中定義了price和type兩個屬性,還有一個DriveDistance方法。我們說,這個car類就像是一個收納包,把和“車”有關的零散屬性、方法都裝進了包里,這就是面向對象的封裝性。
面向對象的三大特性
上一篇我們講的主要內容都符合面向對象的封裝特性。那么問題來了?面向對象難道只有封裝性么?當然不是,作為一個這么難理解的東西,要是只有封裝性都對不起我們死了這么多腦細胞!所以,晴天霹靂來了,面向對象有三大特性,他們分別是:封裝、繼承和多態。
好消息和好消息和好消息,好消息一:封裝我們已講完,所以三座大山我們已經移走了一座,好消息二:由於python的特殊性,多態的應用並不廣泛,所以我們其實還有一座半就勝利了,好消息三:前面那兩條好消息都是真的。閑話少敘,今天咱們就聊聊繼承,移走一座是一座!
正題——面向對象的繼承性
一、繼承
大學同學聚會,同桌吃飯,我們都是人,都有吃飯、喝飲料這些行為,但是畢業之后大家都做了不同的工作,有的當了會計、有的做了程序員,現在我們得到了描述這些同學這個需求,我們一看非常開心,我們可以實現呀,然后寫下了下面左圖的代碼:
我們看上面左側的代碼,這么寫確實實現了我們的需求,但是,寫了那么多行,真正不一樣的只有黃色框框里面的內容,好在大學同學的職業都差不多,這要是高中聚會可就熱鬧了。這個時候,我們就想,有沒有可能我們不重復寫之前的代碼,也實現同樣的功能呢?當然啦!→_→右側這段代碼。看着就簡潔了不少,這就是類的繼承。現在你看着好像有點兒迷糊,沒關系,這里只需要知道有一種簡單的方法可以實現,這種寫法就叫做繼承。具體我們后面還要詳細講。
我們來詳細看看上面這張圖,解釋一下什么叫做繼承,首先在最上面的黃框框里,我們定義了一個類叫做classmate,這個里面放了吃、喝兩個方法,下面我們又定義了兩個類,pythoner和accounting類,里面各寫了一個occupation方法,打印出了人物的職業。我們看到,classmate類和我們之前見到的類並沒什么不同,可是pythoner和accounting類定義的時候,我們看紅框框里寫了classmate類的類名,我們說,這樣就實現了繼承。pythoner和accounting類集成了classmate的所有屬性和方法。
說完了繼承類的定義,我們再來看看實例化和調用,我們看上面右側那張小圖,我們分別實例化了兩個對象,eva和sweet,注意看紅框框里我們實例化的是pythoner和accounting這兩個派生類,但是我們卻可以調用classmate的eat和drink方法,而且我們在occupation中也可以使用父類的name屬性。magic!代碼在下面~

1 class classmate(): 2 def __init__(self,name): 3 self.name = name 4 def eat(self): 5 print '%s is eating.'%self.name 6 def drink(self): 7 print '%s is drinking'%self.name 8 9 10 class pythoner(classmate): 11 def occupation(self): 12 print '%s is a pythoner.'%self.name 13 14 class accounting(classmate): 15 def occupation(self): 16 print '%s is a accounting.'%self.name 17 18 eva = pythoner('eva') 19 eva.occupation() 20 eva.eat() 21 eva.drink() 22 23 sweet = accounting('sweet') 24 sweet.occupation() 25 sweet.eat() 26 sweet.drink()
二、多繼承
現在我們基本可以使用類的繼承描述同坐一桌的同學們了,但是我們現在又有了一個新需求,就是把這一桌的男生和女生分開,男生喝酒,女生喝飲料,這個需求怎么用類來區別呢?先上圖~
我們看上面左側這張圖,由於需求的增加,要求把同桌的男生和女生分開,我又新寫了兩個female和male類重新定義drink方法,並且又定義了新的類fe_pythoner和ma_pythoner,所以這兩個類什么也不做,只是分別繼承female、classmate和male、classmate類。在實例化對象的時候我們使用fe_pythoner和ma_pythoner,我們這樣猜想,這個時候對象eva和sweet是不是應該分別去調用female和male中的drink方法呢?執行下,看看下面的結果。什么鬼?竟然還是輸出了基類的drink方法。再看看右邊,沒錯,我只是在定義基類的時候讓基類繼承了object,它就可以按照我們想要的方法輸出了。
三、經典類的深度優先和新式類的廣度優先
那么原理是什么呢?這個時候我就要盜一張圖來解釋這個問題了:
我們看上面的圖,先放兩句概念上來嚇唬嚇唬你:
- 當類是經典類時,多繼承情況下,會按照深度優先方式查找
- 當類是新式類時,多繼承情況下,會按照廣度優先方式查找
那么什么是經典類和新式類呢?簡而言之,繼承自object的類就叫做新式類,object類是python提供的,現在我們還不需要管它從哪里來,因為讓類中的很多操作變得更合理了,我們以后記着就這么寫就對了。注:下面小伙伴提到,python3.X版本中的類繼承默認就是廣度優先。
下面來說廣度優先和深度優先,首先,B和C兩個類都必須繼承自D,A類又繼承自B、C,就是針對這種情況,沒有為什么。。。背下來!我們對應起來看,這里的基類D就是上例中的classmate,BC就是pythoner和female,A則對應fe_pythoner類。
經典類中:當我們這樣寫:fe_pythoner(pythoner,female),對象調用方法的時候,會先在fe_pythoner里面找,然后依次去找pythoner、classmate、最后再找female。如果找到了,就會執行,並且不再繼續找下去了。所以我們剛剛在左側舉出得栗子中它先找到了classmate中的drink方法,才打印出了同樣的內容。這就是深度優先。
新式類中:當我們這樣寫:fe_pythoner(pythoner,female),對象調用方法的時候,會先在fe_pythoner里面找,然后依次去找pythoner、female、最后再找classmate。如果找到了,就會執行,並且不再繼續找下去了。所以我們剛剛在右側舉得栗子中它先找到了female、或male中的drink方法,就打印了不同的內容。這就是廣度優先。

1 class classmate(object): 2 def __init__(self,name): 3 self.name = name 4 5 def eat(self): 6 print '%s is eating.'%self.name 7 8 def drink(self): 9 print '%s is drinking'%self.name 10 11 class female(classmate): 12 13 def drink(self): 14 print '%s drink orange juice'%self.name 15 16 class male(classmate): 17 18 def drink(self): 19 print '%s drink alcohol'%self.name 20 21 class pythoner(classmate): 22 23 def occupation(self): 24 print '%s is a pythoner.'%self.name 25 26 27 28 class fe_pythoner(pythoner,female): 29 pass 30 class male_pythoner(pythoner,male): 31 pass 32 33 eva = fe_pythoner('eva') 34 eva.drink() 35 36 sweet = male_pythoner('sweet') 37 sweet.drink()
如果上面那些你通通沒搞清楚,也沒關系,在繼承的時候可以直接把female類寫在前面 fe_pythoner(female,pythoner),這么一來不管怎么樣,都是先找female了。
好了,到現在為止我們現在已經移走了面向對象的第二座大山,離勝利又進了一步,感謝努力的自己,又多堅持了一下~~~