不光是Python,大多數面向物件程式語言(諸如C++、Java等)都具備3個典型特徵,即封裝、繼承和多型。其中,本節重點講解Python類的封裝特性,繼承和多型會在後續章節給大家做詳細講解。
簡單的理解封裝(Encapsulation),即在設計類時,刻意地將一些屬性和方法隱藏在類的內部,這樣在使用此類時,將無法直接以“類物件.屬性名”(或者“類物件.方法名(引數)”)的形式呼叫這些屬性(或方法),而只能用未隱藏的類方法間接操作這些隱藏的屬性和方法。
就好比使用電腦,我們只需要學會如何使用鍵盤和滑鼠就可以了,不用關心內部是怎麼實現的,因為那是生產和設計人員該操心的。
注意,封裝絕不是將類中所有的方法都隱藏起來,一定要留一些像鍵盤、滑鼠這樣可供外界使用的類方法。
那麼,類為什麼要進行封裝,這樣做有什麼好處呢?
首先,封裝機制保證了類內部資料結構的完整性,因為使用類的使用者無法直接看到類中的資料結構,只能使用類允許公開的資料,很好地避免了外部對內部資料的影響,提高了程式的可維護性。
除此之外,對一個類實現良好的封裝,使用者只能藉助暴露出來的類方法來訪問資料,我們只需要在這些暴露的方法中加入適當的控制邏輯,即可輕鬆實現使用者對類中屬性或方法的不合理操作。
並且,對類進行良好的封裝,還可以提高程式碼的複用性。
Python類如何進行封裝?
和其它面向物件的程式語言(如C++、Java)不同,Python類中的變數和函式,不是公有的(類似public屬性),就是私有的(類似private),這2種屬性的區別如下:
public:公有屬性的類變數和類函式,在類的外部、類內部以及子類(後續講繼承特性時會做詳細介紹)中,都可以正常訪問;
private:私有屬性的類變數和類函式,只能在本類內部使用,類的外部以及子類都無法使用。
但是,Python並沒有提供public、private這些修飾符。為了實現類的封裝,Python採取了下面的方法:
預設情況下,Python類中的變數和方法都是公有(public)的,它們的名稱前都沒有下劃線(_);
如果類中的變數和函式,其名稱以雙下劃線“__”開頭,則該變數(函式)為私有變數(私有函式),其屬性等同於private。
除此之外,還可以定義以單下劃線“_”開頭的類屬性或者類方法(例如_name、_display(self)),這種類屬性和類方法通常被視為私有屬性和私有方法,雖然它們也能透過類物件正常訪問,但這是一種約定俗稱的用法,初學者一定要遵守。
注意,Python類中還有以雙下劃線開頭和結尾的類方法(例如類的建構函式__init__(self)),這些都是Python內部定義的,用於Python內部呼叫。我們自己定義類屬性或者類方法時,不要使用這種格式。
例如,如下程式示範了Python的封裝機制:
class CLanguage : def setname(self, name): if len(name) < 3: raise ValueError('名稱長度必須大於3!') self.__name = name def getname(self): return self.__name #為 name 配置 setter 和 getter 方法 name = property(getname, setname) def setadd(self, add): if add.startswith("http://"): self.__add = add else: raise ValueError('地址必須以 http:// 開頭') def getadd(self): return self.__add #為 add 配置 setter 和 getter 方法 add = property(getadd, setadd) #定義個私有方法 def __display(self): print(self.__name,self.__add)clang = CLanguage()clang.name = "開課吧廣場"clang.add = "https://topic.kaikeba.com/"print(clang.name)print(clang.add)123456789101112131415161718192021222324252627複製程式碼型別:[python]
程式執行結果為:
開課吧廣場https://topic.kaikeba.com/12複製程式碼型別:[python]
上面程式中,CLanguage將name和add屬性都隱藏了起來,但同時也提供了可操作它們的“視窗”,也就是各自的setter和getter方法,這些方法都是公有(public)的。
不僅如此,以add屬性的setadd()方法為例,透過在該方法內部新增控制邏輯,即透過呼叫startswith()方法,控制使用者輸入的地址必須以“http://”開頭,否則程式將會執行raise語句丟擲ValueError異常。
有關raise的具體用法,後續章節會做詳細的講解,這裡可簡單理解成,如果使用者輸入不規範,程式將會報錯。
透過此程式的執行邏輯不難看出,透過對CLanguage類進行良好的封裝,使得使用者僅能透過暴露的setter()和getter()方法操作name和add屬性,而透過對setname()和setadd()方法進行適當的設計,可以避免使用者對類中屬性的不合理操作,從而提高了類的可維護性和安全性。
細心的讀者可能還發現,CLanguage類中還有一個__display()方法,由於該類方法為私有(private)方法,且該類沒有提供操作該私有方法的“視窗”,因此我們無法在類的外部使用它。換句話說,如下呼叫__display()方法是不可行的:
#嘗試呼叫私有的 display() 方法clang.__display()12複製程式碼型別:[python]
這會導致如下錯誤: