天天看點

Python類及面向對象程式設計【轉】Python類及面向對象程式設計

類是用來建立資料結構和新類型對象的主要機制.本章的主題就是類,面向對象程式設計和設計不是本章的重點。本章假定你具有資料結構的背景知識及一定的面向對象的程式設計經驗(其它面向對象的語言,比如java,c++).(參見第三章,類型和對象 了解對象這個術語及其内部實作的附加資訊)

一個類定義了一系列與其執行個體對象密切關聯的屬性.典型的屬性包括變量(也被稱為類變量)和函數(又被稱為方法).

class語句用來定義一個類.類的主體中語句在類定義同時執行.(如 listing 7.1)

listing 7.1 類

<a href="http://wiki.woodpecker.org.cn/moin/pythonessentialref7#">toggle line numbers</a>

類對象作為一個名字空間,存放在類定義語句運作時建立的對象.例如,account裡的内容可以這樣通路:

需要注意的是, class語句并不建立類的執行個體(例如上邊的例子,并沒有建立任何帳戶).它用來定義所有執行個體都應該有的屬性.

在類中定義的正常方法的第一個參數總是該類的執行個體,通常這個參數記為self。你也可能用其它任何合法的變量名,不過為了符合慣例,你最好還是用self. 類中定義的變量,即類變量,如account_type, 它被所有該類的執行個體共享. 雖然類定義了一個名字空間,但這個名字空間并不是為類主體中的代碼服務的.是以在類中引用一個類的屬性必須使用類的全名:

最後,你不能定義一個不操作執行個體的方法:

=======================================================================================

靜态方法: 可以直接被類或類執行個體調用。它沒有正常方法那樣的特殊行為(綁定、非綁定、預設的第一個參數規則等等)。你完全可以将靜态方法當成一個用屬性引用方式調用的普通函數。任何時候定義靜态方法都不是必須的(靜态方法能實作的功能都可以通過定義一個普通函數來實作). 有些程式員認為,當有一堆函數僅僅為某一特定類編寫時,将這些函數包裝成靜态這種方式可以提供使用上的一緻性。

根據python2.4最新提供的新文法,你可以用下面的方式建立一個靜态方法:

注:staticmethod是一個内建函數,用來将一個方法包裝成靜态方法,在2.4以前版本,隻能用下面這種方式定義一個靜态方法(不再推薦使用):

這種方法在函數定義本身比較長時經常會忘記後面這一行.

類方法 一個類方法就可以通過類或它的執行個體來調用的方法, 不管你是用類來調用這個方法還是類執行個體調用這個方法,該方法的第一個參數總是定義該方法的類對象。 記住:方法的第一個參數都是類對象而不是執行個體對象. 按照慣例,類方法的第一個形參被命名為 cls. 任何時候定義類方法都不是必須的(類方法能實作的功能都可以通過定義一個普通函數來實作,隻要這個函數接受一個類對象做為參數就可以了). 你可以象下面這樣來生成一個類方法:

注:classmethod是一個内建函數,用來将一個方法封裝成類方法,在2.4以前版本,你隻能用下面的方式定義一個類方法:

并沒有人要求必須封裝後的方法名字必須與封裝前一緻,但建議你總是這樣做(如果你使用python2.4版本以下時). 這種方法在函數定義本身比較長時經常會忘記後面這一行.

增補部分至此結束

像調用函數一樣調用類,可以得到類的執行個體。生成執行個體的過程會自動調用類的__init__方法(如果你的類定義了這個方法的話)。

執行個體建立之後,就可以使用點(.)操作符來通路它的屬性和方法:

在系統内部,每個類執行個體都擁有一個字典(即執行個體的 __dict__ 屬性,在第三章中有介紹).這個字典包含每個執行個體的資訊.例如:

若一個執行個體的屬性被修改,這個字典也随之改變.上例中,屬性通過account類中定義的方法__init()__, deposit(),以及withdraw()中對self變量指派被改變. 不過對于類執行個體可以随時添加私有屬性。

屬性的指派總是發生在執行個體字典中,而屬性通路則比屬性指派複雜一些。當通路一個屬性的時候,解釋器首先在執行個體的字典中搜尋,若找不到則去建立這個執行個體的類的字典中搜尋,若還找不到就到類的基類中搜尋(在後邊 '繼承' 一節中會講到),如果還找不到最後會嘗試調用類的__getattr__方法來擷取屬性值(若類中定義了該方法的話).如果這個過程也失敗,則引發attributeerror異常

所有執行個體都是引用記數的.若一個執行個體引用記數變成零,該執行個體就被銷毀.當執行個體将被銷毀前,解釋器會搜尋該對象的 __del__方法并調用它。但在實際應用中,極少有需要給一個類定義__del__方法, 除非這個對象在銷毀前需要執行一些清除操作(如關閉檔案,斷開網絡,或者釋放其他系統資源).即使是在這種情況下,依賴__del__()來執行清除和關閉操作也是危險的,因為不能保證在解釋器關閉時會自動調用這個方法.更好的選擇是定義一個close()方法,在需要時顯式的調用這個方法來執行這個過程. 最後注意一點, 如果一個執行個體擁有__del__方法,則它永遠不會被python的垃圾收集器回收(這也是不推薦定義 __del__()的理由).關于垃圾回收請參閱附錄a中的gc子產品。

有時會使用del語句來删除對象的引用,如果這導緻該對象引用記數變為零,就會自動調用__del__(). del語句并不直接調用__del__().

繼承(inheritance)是建立新類的機制之一,它通過一個已有類進行修改和擴充來生成新類。這個原始的類被稱為基類(base class)或超類(superclass).新生成的類稱為該類的派生類(derived class)或子類(subclass).當通過繼承建立一個類時,它會自動'繼承'在基類中定義的屬性。一個子類也可以重新定義父類中已有的屬性或定義新的屬性.

python支援多繼承,如果一個類有多個父類,在class語句中就使用逗号來分隔這個父類清單。例如:

當搜尋在基類中定義的某個屬性時,python采用深度優先的原則、按照子類定義中的基類順序進行搜尋。**注意**(new-style類已經改變了這種行為)。上邊例子中,如果通路 a.varb ,就會按照a-b-d-c-d這個順序進行搜尋,隻要找到就停止搜尋.若有多個基類定義同一屬性的情況,則隻使用第一個被找到屬性值:

重要提示:新舊對象模型的差異:

如果一個子類定義了一個和基類具有相同名稱的屬性,則子類的執行個體将使用子類中定義的屬性.如果需要通路原來的屬性,則必須使用全名來限制通路區域:

需要注意的一點是子類執行個體的初始化.當一個子類執行個體被建立時, 基類的 __init__()方法并不會被自動調用.也就是子類必須自力更生來解決執行個體的初始化.例如:

__del__() 與 __init__() 類似.

python通過上文中提到的屬性查詢規則來實作多态.當使用obj.method() 來通路一個方法時,方法的搜尋順序為:執行個體的 __dict__ 屬性,執行個體的類定義,基類. 第一個被找到的方法被執行。

預設情況下,所有的屬性都是'公開'的.這意味着一個類的所有屬性均可不受任何限制的通路.這也意味着基類中定義的所有内容都能被子類繼承。 在面向對象程式設計實踐中,這種行為是我們不希望的。因為它不但暴露了對象的内部實作,而且容易在派生類對象及基類對象之間産生名字空間沖突。

要解決這個問題,隻需要在類中将需要隐藏的屬性名字以兩個下劃線開頭,例如 __foo。這樣系統會自動實時生成一個新的名字 _classname__foo 并用于内部使用。這樣在某種程度上就提供了私有屬性(其實這個 _classname__foo 仍然是不受限制通路的嘿嘿),也解決了名字空間沖突的問題.例如:

這是一個小技巧,并沒有真正阻止通路一個類的*私有*屬性.如果已知一個類的名稱和它某個私有屬性的名稱,我們還是可以使用_classname__foo 來通路到這個屬性.(這不是bug,因為在某些特定的場合這非常有用,比如調試時,是以系統一直保留這個所謂的*問題*)

使用者自定義對象可以通過在類中實作特殊方法(第三章中已介紹)來重載python内建操作符.例如 listing 7.2 中的類,它使用标準的數學運算符實作了複數的運算及類型轉換.

listing 7.2 數學運算及類型轉換

在這個例子中,有一些值得研究的地方:

首先__repr__() 用于傳回對象的表達式字元串表示,這個傳回字元串可以用于再次得到該對象.在本例中,會建立一個類似"complex(r,i)"的字元串.另外__str__()方法建立一個字元串用于較美觀的輸出。(通常用于print語句)

然後,要處理複數在運算符左邊或右邊這兩種情況,必須同時提供 __op__()和 __rop__()方法.

最後, __ceorco__ 方法用于處理混合類型運算.在本例中,其他的數值類型均被轉換為複數,這樣才可以繼續進行複數的運算.

内建函數isinstance(obj ,cname)用來測試obj對象是否是cname的執行個體。.如果是,函數就傳回true.例如:

同樣地,内建函數issubclass(a ,b)用來測試類a是否是類b的子類:

isinstance()函數也可以用于檢查任意内建類型:

這是一個被推薦的類型檢查方法,這樣類型和類的差别就可以忽略.

歡迎加群互相學習,共同進步。qq群:ios: 58099570 | android: 330987132 | go:217696290 | python:336880185 | 做人要厚道,轉載請注明出處!http://www.cnblogs.com/sunshine-anycall/archive/2012/06/18/2554156.html