上一節(jié)課我們講解了Python面向?qū)ο缶幊痰幕A(chǔ)入門知識,這一節(jié)課我們繼續(xù)來討論面向?qū)ο缶幊滔嚓P(guān)的內(nèi)容。 另外上節(jié)課很多伙伴要了視頻學習教程,提醒大家哈,認真的看完,不清楚的地方,可以再說! 可見性和屬性裝飾器 在很多面向?qū)ο缶幊陶Z言中,對象的屬性通常會被設(shè)置為私有(private)或受保護(protected)的成員,簡單的說就是不允許直接訪問這些屬性;對象的方法通常都是公開的(public),因為公開的方法是對象能夠接受的消息,也是對象暴露給外界的調(diào)用接口,這就是所謂的訪問可見性。在Python中,可以通過給對象屬性名添加前綴下劃線的方式來說明屬性的訪問可見性,例如,可以用__name表示一個私有屬性,_name表示一個受保護屬性,代碼如下所示。
上面代碼的最后一行會引發(fā)AttributeError(屬性錯誤)異常,異常消息為:'Student' object has no attribute '__name'。由此可見,以__開頭的屬性__name是私有的,在類的外面無法直接訪問,但是類里面的study方法中可以通過self.__name訪問該屬性。 需要提醒大家的是,Python并沒有從語法上嚴格保證私有屬性的私密性,它只是給私有的屬性和方法換了一個名字來阻撓對它們的訪問,事實上如果你知道更換名字的規(guī)則仍然可以訪問到它們,我們可以對上面的代碼稍作修改就可以訪問到私有的。
Python中做出這樣的設(shè)定是基于一句名言:“We are all consenting adults here”(大家都是成年人)。Python語言的設(shè)計者認為程序員要為自己的行為負責,而不是由Python語言本身來嚴格限制訪問可見性,而大多數(shù)的程序員都認為開放比封閉要好,把對象的屬性私有化并不是必須的東西。 Python中可以通過property裝飾器為“私有”屬性提供讀取和修改的方法,裝飾器通常會放在類、函數(shù)或方法的聲明之前,通過一個@符號表示將裝飾器應(yīng)用于類、函數(shù)或方法。裝飾器的概念我們會在稍后的課程中以專題的形式為大家講解,這里我們只需要了解property裝飾器的用法就可以了。
在實際項目開發(fā)中,我們并不經(jīng)常使用私有屬性,屬性裝飾器的使用也比較少,所以上面的知識點大家簡單了解一下就可以了。 動態(tài)屬性 Python是一門動態(tài)語言,維基百科對動態(tài)語言的解釋是:“在運行時可以改變其結(jié)構(gòu)的語言,例如新的函數(shù)、對象、甚至代碼可以被引進,已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化。動態(tài)語言非常靈活,目前流行的Python和JavaScript都是動態(tài)語言,除此之外如PHP、Ruby等也都屬于動態(tài)語言,而C、C++等語言則不屬于動態(tài)語言”。 在Python中,我們可以動態(tài)為對象添加屬性,這是Python作為動態(tài)類型語言的一項特權(quán),代碼如下所示。需要提醒大家的是,對象的方法其實本質(zhì)上也是對象的屬性,如果給對象發(fā)送一個無法接收的消息,引發(fā)的異常仍然是AttributeError。
如果不希望在使用對象時動態(tài)的為對象添加屬性,可以使用Python的__slots__魔法。對于Student類來說,可以在類中指定__slots__ = ('name', 'age'),這樣Student類的對象只能有name和age屬性,如果想動態(tài)添加其他屬性將會引發(fā)異常,代碼如下所示。
靜態(tài)方法和類方法 之前我們在類中定義的方法都是對象方法,換句話說這些方法都是對象可以接收的消息。除了對象方法之外,類中還可以有靜態(tài)方法和類方法,這兩類方法是發(fā)給類的消息,二者并沒有實質(zhì)性的區(qū)別。在面向?qū)ο蟮氖澜缋铮磺薪詾閷ο?,我們定義的每一個類其實也是一個對象,而靜態(tài)方法和類方法就是發(fā)送給類對象的消息。那么,什么樣的消息會直接發(fā)送給類對象呢? 舉一個例子,定義一個三角形類,通過傳入三條邊的長度來構(gòu)造三角形,并提供計算周長和面積的方法。計算周長和面積肯定是三角形對象的方法,這一點毫無疑問。但是在創(chuàng)建三角形對象時,傳入的三條邊長未必能構(gòu)造出三角形,為此我們可以先寫一個方法來驗證給定的三條邊長是否可以構(gòu)成三角形,這種方法很顯然就不是對象方法,因為在調(diào)用這個方法時三角形對象還沒有創(chuàng)建出來。我們可以把這類方法設(shè)計為靜態(tài)方法或類方法,也就是說這類方法不是發(fā)送給三角形對象的消息,而是發(fā)送給三角形類的消息,代碼如下所示。
上面的代碼使用staticmethod裝飾器聲明了is_valid方法是Triangle類的靜態(tài)方法,如果要聲明類方法,可以使用classmethod裝飾器。可以直接使用類名.方法名的方式來調(diào)用靜態(tài)方法和類方法,二者的區(qū)別在于,類方法的第一個參數(shù)是類對象本身,而靜態(tài)方法則沒有這個參數(shù)。簡單的總結(jié)一下,對象方法、類方法、靜態(tài)方法都可以通過類名.方法名的方式來調(diào)用,區(qū)別在于方法的第一個參數(shù)到底是普通對象還是類對象,還是沒有接受消息的對象。靜態(tài)方法通常也可以直接寫成一個獨立的函數(shù),因為它并沒有跟特定的對象綁定。 繼承和多態(tài) 面向?qū)ο蟮木幊陶Z言支持在已有類的基礎(chǔ)上創(chuàng)建新類,從而減少重復代碼的編寫。提供繼承信息的類叫做父類(超類、基類),得到繼承信息的類叫做子類(派生類、衍生類)。例如,我們定義一個學生類和一個老師類,我們會發(fā)現(xiàn)他們有大量的重復代碼,而這些重復代碼都是老師和學生作為人的公共屬性和行為,所以在這種情況下,我們應(yīng)該先定義人類,再通過繼承,從人類派生出老師類和學生類,代碼如下所示。
繼承的語法是在定義類的時候,在類名后的圓括號中指定當前類的父類。Python語言允許多重繼承,也就是說一個類可以有一個或多個父類,關(guān)于多重繼承的問題我們在后面會有更為詳細的討論。在子類的初始化方法中,我們可以通過super().__init__()來調(diào)用父類初始化方法,super函數(shù)是Python內(nèi)置函數(shù)中專門為獲取當前對象的父類對象而設(shè)計的。從上面的代碼可以看出,子類除了可以通過繼承得到父類提供的屬性和方法外,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力。在實際開發(fā)中,我們經(jīng)常會用子類對象去替換掉一個父類對象,這是面向?qū)ο缶幊讨幸粋€常見的行為,也叫做“里氏替換原則”(Liskov Substitution Principle)。 子類繼承父類的方法后,還可以對方法進行重寫(重新實現(xiàn)該方法),不同的子類可以對父類的同一個方法給出不同的實現(xiàn)版本,這樣的方法在程序運行時就會表現(xiàn)出多態(tài)行為(調(diào)用相同的方法,做了不同的事情)。多態(tài)是面向?qū)ο缶幊讨凶罹璧牟糠?,當然也是對初學者來說最難以理解和靈活運用的部分,我們會在下一節(jié)課中用專門的例子來講解多態(tài)這個知識點。 簡單的總結(jié) Python是動態(tài)語言,Python中的對象可以動態(tài)的添加屬性。在面向?qū)ο蟮氖澜缰校?span style="-webkit-tap-highlight-color: transparent;box-sizing: border-box;font-weight: 700;margin: 0px;padding: 0px;border: 0px;">一切皆為對象,我們定義的類也是對象,所以類也可以接收消息,對應(yīng)的方法是類方法或靜態(tài)方法。通過繼承,我們可以從已有的類創(chuàng)建新類,實現(xiàn)對已有類代碼的復用。 |
|