歡迎光臨
我們一直在努力

寫給小白的Python之017:物件導向-封裝、繼承、多型

文章摘要: 子類重寫父類的屬性和方法 子類和父類的方法名和屬性名相同呼叫不同子類物件的相同父類方法

1.  封裝

物件導向三大特性:封裝、繼承、多型

封裝(Encapsulation) 這是定義類的 準則,單個類。根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中。

封裝的意義:

1.將屬性和方法放到一起做為一個整體,然後通過例項化物件來處理;

2.隱藏內部實現細節,只需要和物件及其屬性和方法互動就可以了;

3.對類的屬性和方法增加 訪問許可權控制。

2.  繼承

繼承:這是設計類的 技巧,父與子。

  • 主要體現是實現程式碼的 重用,相同的程式碼不需要重複的編寫;
  • 子類可以在父類功能上進行重寫,擴充套件類的功能。

在物件導向程式設計中,當我們已經建立了一個類,而又想再建立一個與之相似的類,比如新增幾個方法,或者修改原來的方法,這時我們不必從頭開始,可以從原來的類派生出一個新的類, 我們把原來的類稱為父類或基類,而派生出的類稱為子類或派生類,子類繼承了父類的所有資料和方法。

從技術上說, OOP裡繼承最主要的用途是實現多型 。對於多型而言,重要的是 介面繼承性 ,屬性和行為是否存在繼承性,這是不一定的。事實上, 大量工程實踐表明,重度的行為繼承會導致系統過度複雜和臃腫,反而會降低靈活性。因此現在比較提倡的是 基於介面的輕度繼承理念 。這種模型裡因為父類(介面類)完全沒有程式碼,因此根本談不上什麼程式碼複用。

2.1 單繼承

子類的定義如下:

class 子類名 ( 父類名 ):

父類名 BaseClassName 對於子類來說必須是可見的。也可以繼承在其他模組中定義的父類:

class 子類名 ( 模組名 . 父類名 ):

對於子類的屬性引用:首先會在當前的子類中搜索,如果沒有找到,則會遞迴地去父類中尋找。

從C++術語上講,Python 類中所有的方法都是 vitual 的,所以 子類可以覆寫(override)父類的方法 。在子類中一個覆寫的方法可能需要呼叫父類的方法,可以通過以下方式:

父類名.方法(self, arguments)

2.2 多繼承

Python支援多繼承,一個多繼承的定義形如:

class 子類名 ( 父類名 1, 父類名 2, 父類名 3):

多個父類有同名屬性和方法

子類的魔法屬性__mro__決定了屬性和方法的查詢順序。 如果多個父類中有同名的 屬性和方法,則預設使用第一個父類的屬性和方法(根據類的魔法屬性mro的順序來查詢)

子類重寫父類的屬性和方法

子類和父類的方法名和屬性名相同,則預設使用子類的 ,叫 子類重寫父類的同名方法和屬性。

使用重寫的目的:當子類發現父類的大部分功能都能滿足需求,但是有一些功能不滿足,則子類可以重寫父類方法。

2.3 呼叫父類方法

重寫之後,如果發現仍然需要父類方法,則可以強制呼叫父類方法。

方法 1. 指定執行父類的方法

無論何時何地,self都表示是子類的物件。在呼叫父類方法時,通過傳遞self引數,來控制方法和屬性的訪問修改。通常用於多繼承。

  • 父類名 . 父類方法 (self, 引數列表 )
  • 父類名().屬性/方法   # 不推薦這樣訪問父類的例項屬性,相當於建立了一個新的父類物件

子類繼承了多個父類,如果父類類名修改了,那麼子類也要涉及多次修改。而且需要重複寫多次呼叫,顯得程式碼臃腫。

方法 2. super() 帶引數版本

只支援新式類,支援Python2和3。工作中使用這個。

  • super( 子類名 , self). 父類方法 ( 引數列表 )

# super(子類名, self).__init__() # 執行父類的 __init__方法

# self.方法名()

示例:

class Animal:

    def __init__(self, age):  # 1.父類有時候需要接收一些引數
        self.age = age


class Cat(Animal):

    def __init__(self, age):  # 3.一般情況下,父類需要的引數,子類也是動態來獲取
        self.name = '伊麗莎白'
        super(Cat, self).__init__(age)  # 2.子類需要為父類傳遞實參


cat = Cat(12)  # 4. 根據子類需求,傳遞對應的實參
print(cat.age)  # 12

方法 3. super() 的簡化版

只支援新式類,只支援Python3

  • super(). 父類方法 ( 引數列表 )   # 執行父類的 例項方法

super().__init__()  # 執行父類的 __init__方法

知識點:

  • 使用super() 可以逐一呼叫所有的父類方法,並且只執行一次。呼叫順序遵循 mro 類屬性的順序。
  • 注意:如果繼承了多個父類,且父類都有同名方法,則預設只執行第一個父類的(同名方法只執行一次,目前super()不支援執行多個父類的同名方法)
  • super() 在Python2.3之後纔有的機制,用於通常單繼承的多層繼承。

3.  多型

多型是指對不同型別的變數進行相同的操作,它會根據物件(或類)型別的不同而表現出不同的行為。即 不同類的物件呼叫相同方法,產生不同的結果。

多型( Polymorphism ):

  • 不同的子類物件呼叫相同的父類方法,產生不同的 執行結果;父類能工作的地方,子類都能工作;
  • 多型以繼承和重寫父類方法為前提;
  • 多型是呼叫方法的技巧,不會影響到類的內部設計。

多型的好處:

在保證安全性的前提下,提高了方法呼叫的靈活性。

多型的實現:

1.定義一個父類

2.定義多個子類,並重寫父類的方法

3.傳遞子類物件給呼叫者,不同子類物件能產生不同執行效果

示例:

class Dog(object):
    def work(self):  # 父類提供統一的方法,哪怕是空方法
        pass

class ArmyDog(Dog):   # 繼承 Dog
    def work(self):  # 子類重寫方法,並且處理自己的行為
        print('追擊敵人')

class DrugDog(Dog):
    def work(self):
        print('追查毒品')

class Person(object):
    def work_with_dog(self, dog):
        dog.work()    # 使用小狗可以根據物件的不同而產生不同的執行效果, 保障了程式碼的穩定性
 

# 子類物件可以當作父類來使用
dog = Dog()
print(isinstance(dog, Dog))  # True

ad = ArmyDog()
print(isinstance(ad, Dog))  # True

dd = DrugDog()
print(isinstance(dd, Dog))  # True

p = Person()
p.work_with_dog(dog)
p.work_with_dog(ad)  # 同一個方法,只要是 Dog 的子類就可以傳遞,提供了程式碼的靈活性
p.work_with_dog(dd)  # 並且傳遞不同物件,最終 work_with_dog 產生了不同的執行效果

# 最終效果
# Person 類中只需要呼叫 Dog 物件 work() 方法,而不關心具體是 什麼狗
# work() 方法是在 Dog 父類中定義的,子類重寫並處理不同方式的實現
# 在程式執行時,傳入不同的 Dog 物件作為實參,就會產生不同的執行效果

多型總結:

# 定義:多型是一種使用物件的方式,子類重寫父類方法,呼叫不同子類物件的相同父類方法,可以產生不同的執行結果。

# 好處:呼叫靈活,有了多型,更容易編寫出通用的程式碼,做出通用的程式設計,以適應需求的不斷變化!

鴨子型別:

動態語言的「鴨子型別」(duck typeing),它並不要求嚴格的繼承體系,一個物件只要「看起來像鴨子,走起路來像鴨子」,那它就可以被看做是鴨子。即只要一個物件相似的屬性和方法。

動態語言的鴨子型別特點決定了繼承不像靜態語言那樣是必須的。

型別檢查是毀掉多型的利器, 比如type、 isinstance以及isubclass函式,所以,一定要慎用這些型別檢查函式。

未經允許不得轉載:頭條楓林網 » 寫給小白的Python之017:物件導向-封裝、繼承、多型