Python面向對象中類的三大特性,即封裝、繼承與多態,同時也是所有語言在面向對象編程中的重要特性,下面用實際的代碼部分針對每一種特性做出說明。
一、封裝
顧名思義,封裝即把我們想要的內容封裝到某個地方,因此在這裏記錄兩個問題,一是如何封裝、二是如何調用封裝的內容。
1、封裝數據
class Company: def __init__(self, dept, leader): self.dept = dept self.leader = leader if __name__ == "__main__": # 將公司名和Leader分別封裝到對象obj1、obj2中self的dept和leader屬性中 obj1 = Company("A","Kevin") obj2 = Company("B","Jone")
2、調用數據
調用數據又可以分爲兩種方式:通過對象直接調用、通過self間接調用
class Company: def __init__(self, dept, leader): self.dept = dept self.leader = leader def show(self): print self.dept print self.leader if __name__ == "__main__": obj1 = Company("A","Kevin") obj2 = Company("B","Jone") # 通過對象直接調用封裝的數據 print obj1.dept print obj1.leader # 通過self來間接調用,self即爲對象本身 obj1.show() obj2.show()
總結封裝特性:即通過類的構造方法將我們想要的內容封裝到對象中,調用時通過對象直接調用、或通過self指向的類對象來間接調用。
二、繼承
顧名思義繼承即爲擁有了某些不屬於自己的東西,Python當中類的繼承同樣如此,通過繼承可以爲我們的類擴展更多的功能,同時在代碼模塊化開發、升級的過程中,繼承也是一種規模化、高效化的生產方式。
不同於其他語言,Python中的類還具有多繼承的特性,即可以有多個父類,下面通過代碼來展示類的單繼承、多繼承以及繼承過程中類成員方法的尋找順序。
1、單繼承
class Scale: def check(self): if self.count_person > 500: print "%s is big company." %self.name else: print "%s is small company." %self.name class Company(Scale): def __init__(self, name, count): self.name = name self.count_person = count if __name__ == "__main__": my_company = Company("ABC", 800) # Company類中並沒有check方法,代碼會向上自動檢測父類中是否存在check方法,結果在Scale類中找到了 my_company.check()
2、多繼承
class Scale: def check(self): if self.count_person > 500: return "%s is big company." %self.name else: return "%s is small company." %self.name class Detail: def show(self, scale): print "%s, This company has %s persons." %(scale, self.count_person) class Company(Scale, Detail): def __init__(self, name, count): self.name = name self.count_person = count if __name__ == "__main__": my_company = Company("ABC", 800) company_scale = my_company.check() my_company.show(company_scale)
上述代碼中Company分別繼承了父類Scale、Detail,在實例化對象my_company後,我們分別用到了父類中的check和show方法。
3、類的繼承順序
與其說類的繼承順序,不如說是對象執行方法時,該方法在類中的查找順序,之所以用到繼承,那麼一定是需要用到父類當中的某個方法,那麼這個方法在類中就要遵循一定的查找順序,簡而言之可以概括爲:先下後上、從左到右,下面通過代碼來進行說明。
class Scale: def check(self): if self.count_person > 500: print "%s is big company." %self.name else: print "%s is small company." %self.name class Detail: def show(self, scale): print "%s, This company has %s persons." %(scale, self.count_person) class Company(Scale, Detail): def __init__(self, name, count): self.name = name self.count_person = count def show(self): print "This is display from class Company, our company has %s persons." %self.count_person if __name__ == "__main__": my_company = Company("ABC", 800) my_company.check() my_company.show()
1) 創建實例化對象 my_company,並封裝了公司名稱、人數等基本內容到對象中。
2) 調用類中的check方法,此時程序發現本類中沒有check方法,於是開始向上尋找,最終在Detail類中找到。
3) 調用類中的show方法,此時程序發現本類中存在show方法,直接調用。
下面來看稍微複雜些的情況,父類又繼承父類,同時一個父類被多個子類同時繼承,關係如下圖所示。
C_5類繼承了C_3和C_4類,同時C_3類、C_4類又分別繼承C_1類和C_2類,C_1、C_2類又共同繼承了C_0類,我們來看通過對象來調用C_5類中的special方法時,該方法在類中是按照什麼順序查找的。
class C_0: def special(self): print "I am special from C_0" class C_1(C_0): def f1(self): pass class C_2(C_0): def special(self): print 'I am special from C_2' class C_3(C_1): def f3(self): pass class C_4(C_2): def f4(self): pass class C_5(C_3, C_4): def f5(self): pass if __name__ == "__main__": obj = C_5() obj.special()
代碼最終打印了“I am special from C_0”,由此可見代碼是從左邊開始查找,一直找到了最頂層的C_0的special方法,但在Python3版本中,同樣的情況,代碼走到C_1時就會停止向上查找,而是回到C_5中繼續向右查找,最終執行了C_2類中的special方法,打印了“I am special from C_2”,這是兩個版本中的一點區別。
最後來看一種更爲複雜的繼承關係
代碼如下:
class C_1: def show(self): self.detail() class C_2(C_1): def __init__(self): self.title = "This is C_2" def show(self): self.detail() def detail(self): print 'I am detail from C_2' class C_3(C_2): def __init__(self): self.title = "This is C_3" class C_4(): def detail(self): print 'I am detail from C_4' class C_5(C_4, C_3): pass my_data = C_5() my_data.show()print my_data.title
這裏我們討論兩個內容:
1、對象my_data中的show方法最終打印了什麼內容。
2、對象my_data中的title屬性中的內容是什麼。
我們來分析一下整個對象實例化及其方法的調用過程
1、創建對象my_data,注意此處的C_5後面是加括號的,即創建實例化對象,此時代碼按照從下到上、從左到右的順序查找構造方法。
2、最終在C_3類中找到了__init__構造方法,並執行其中的對象屬性賦值。
3、調用對象my_data中的show方法,此時代碼依然按照下至上,左至右的順序查找整個類中的show方法。
4、最終在最頂層的C_1類中找到了show方法,並執行其中的start方法。
5、代碼繼續從self所指的my_data對象中重新開始查找start方法。
6、從C_5中查找start方法,未找到--->C_4中,未找到--->C_3中,未找到--->C_2中,找到start方法,並執行其中的detail方法。
7、代碼繼續從self所指的my_data對象中重新開始查找detail方法。
8、從C_5中查找detail方法,未找到--->C_4中,找到了detail方法,並打印“I am detail from C_4”
9、由於實例化類的時候,代碼自動執行了C_3中的構造方法,說以title的值爲“This is C_3”
最終結果爲:
I am detail from C_4
This is C_3
總結:在類的繼承中我們一直討論從下至上,從左到右的順序來查找對應的方法,但實際情況中,父類往往又調用了其他子類當中的方法,此時就要注意self本身的含義,self即指該類實例化對象本身,類中如果調用了self對應的方法,也就是要從對象最底層重新開始尋找對應的方法,例如上述代碼中,不能誤以爲最終C_2中的show方法調用本類裏的detail方法,要注意detail方法是要被重新查找的。理解這一點至關重要,否則我們在閱讀Python項目的源碼時會遇到很多困難。
三、多態
Python當中的多態我認爲是非常好理解的,顧名思義多態就是多種形態,在python中我們在對函數、或是類進行傳參的時候,我們往往不會特別關心傳遞的參數到底是什麼類型,例如它可以是字符串、可以是整型、列表、字典、甚至是任何對象,但是在其他編程語言中,參數往往需要被明確定義是什麼類型,這也是Python與其他語言的區別吧。