不光是Python/ target=_blank class=infotextkey>Python,大多數(shù)面向?qū)ο缶幊陶Z言(諸如C++、JAVA等)都具備3個(gè)典型特征,即封裝、繼承和多態(tài)。其中,本節(jié)重點(diǎn)講解Python類的封裝特性,繼承和多態(tài)會(huì)在后續(xù)章節(jié)給大家做詳細(xì)講解。
簡單的理解封裝(Encapsulation),即在設(shè)計(jì)類時(shí),刻意地將一些屬性和方法隱藏在類的內(nèi)部,這樣在使用此類時(shí),將無法直接以“類對象.屬性名”(或者“類對象.方法名(參數(shù))”)的形式調(diào)用這些屬性(或方法),而只能用未隱藏的類方法間接操作這些隱藏的屬性和方法。
就好比使用電腦,我們只需要學(xué)會(huì)如何使用鍵盤和鼠標(biāo)就可以了,不用關(guān)心內(nèi)部是怎么實(shí)現(xiàn)的,因?yàn)槟鞘巧a(chǎn)和設(shè)計(jì)人員該操心的。
注意,封裝絕不是將類中所有的方法都隱藏起來,一定要留一些像鍵盤、鼠標(biāo)這樣可供外界使用的類方法。
那么,類為什么要進(jìn)行封裝,這樣做有什么好處呢?
首先,封裝機(jī)制保證了類內(nèi)部數(shù)據(jù)結(jié)構(gòu)的完整性,因?yàn)槭褂妙惖挠脩魺o法直接看到類中的數(shù)據(jù)結(jié)構(gòu),只能使用類允許公開的數(shù)據(jù),很好地避免了外部對內(nèi)部數(shù)據(jù)的影響,提高了程序的可維護(hù)性。
除此之外,對一個(gè)類實(shí)現(xiàn)良好的封裝,用戶只能借助暴露出來的類方法來訪問數(shù)據(jù),我們只需要在這些暴露的方法中加入適當(dāng)?shù)目刂七壿嫞纯奢p松實(shí)現(xiàn)用戶對類中屬性或方法的不合理操作。
并且,對類進(jìn)行良好的封裝,還可以提高代碼的復(fù)用性。
Python類如何進(jìn)行封裝?
和其它面向?qū)ο蟮木幊陶Z言(如C++、Java)不同,Python類中的變量和函數(shù),不是公有的(類似public屬性),就是私有的(類似private),這2種屬性的區(qū)別如下:
public:公有屬性的類變量和類函數(shù),在類的外部、類內(nèi)部以及子類(后續(xù)講繼承特性時(shí)會(huì)做詳細(xì)介紹)中,都可以正常訪問;
private:私有屬性的類變量和類函數(shù),只能在本類內(nèi)部使用,類的外部以及子類都無法使用。
但是,Python并沒有提供public、private這些修飾符。為了實(shí)現(xiàn)類的封裝,Python采取了下面的方法:
默認(rèn)情況下,Python類中的變量和方法都是公有(public)的,它們的名稱前都沒有下劃線(_);
如果類中的變量和函數(shù),其名稱以雙下劃線“__”開頭,則該變量(函數(shù))為私有變量(私有函數(shù)),其屬性等同于private。
除此之外,還可以定義以單下劃線“_”開頭的類屬性或者類方法(例如_name、_display(self)),這種類屬性和類方法通常被視為私有屬性和私有方法,雖然它們也能通過類對象正常訪問,但這是一種約定俗稱的用法,初學(xué)者一定要遵守。
注意,Python類中還有以雙下劃線開頭和結(jié)尾的類方法(例如類的構(gòu)造函數(shù)__init__(self)),這些都是Python內(nèi)部定義的,用于Python內(nèi)部調(diào)用。我們自己定義類屬性或者類方法時(shí),不要使用這種格式。
例如,如下程序示范了Python的封裝機(jī)制:
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)
#定義個(gè)私有方法
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復(fù)制代碼類型:[python]
程序運(yùn)行結(jié)果為:
開課吧廣場
https://topic.kaikeba.com/12復(fù)制代碼類型:[python]
上面程序中,CLanguage將name和add屬性都隱藏了起來,但同時(shí)也提供了可操作它們的“窗口”,也就是各自的setter和getter方法,這些方法都是公有(public)的。
不僅如此,以add屬性的setadd()方法為例,通過在該方法內(nèi)部添加控制邏輯,即通過調(diào)用startswith()方法,控制用戶輸入的地址必須以“http://”開頭,否則程序?qū)?huì)執(zhí)行raise語句拋出ValueError異常。
有關(guān)raise的具體用法,后續(xù)章節(jié)會(huì)做詳細(xì)的講解,這里可簡單理解成,如果用戶輸入不規(guī)范,程序?qū)?huì)報(bào)錯(cuò)。
通過此程序的運(yùn)行邏輯不難看出,通過對CLanguage類進(jìn)行良好的封裝,使得用戶僅能通過暴露的setter()和getter()方法操作name和add屬性,而通過對setname()和setadd()方法進(jìn)行適當(dāng)?shù)脑O(shè)計(jì),可以避免用戶對類中屬性的不合理操作,從而提高了類的可維護(hù)性和安全性。
細(xì)心的讀者可能還發(fā)現(xiàn),CLanguage類中還有一個(gè)__display()方法,由于該類方法為私有(private)方法,且該類沒有提供操作該私有方法的“窗口”,因此我們無法在類的外部使用它。換句話說,如下調(diào)用__display()方法是不可行的:
#嘗試調(diào)用私有的 display() 方法
clang.__display()12復(fù)制代碼類型:[python]
這會(huì)導(dǎo)致如下錯(cuò)誤:
Traceback (most recent call last):
File "D:python3.61.py", line 33, in <module>
clang.__display()
AttributeError: 'CLanguage' object has no attribute '__display'