Python面向對象之類的封裝、繼承與多態

  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方法,直接調用。

下面來看稍微複雜些的情況,父類又繼承父類,同時一個父類被多個子類同時繼承,關係如下圖所示。

wKiom1d8ZomQ3yqiAAAjpT-5oi4705.png-wh_50

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”,這是兩個版本中的一點區別。

最後來看一種更爲複雜的繼承關係

wKiom1d8ZpbwJZqbAABjAyyaLyw227.png-wh_50

代碼如下:

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與其他語言的區別吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章