1. 封裝 面向對象三大特性:封裝、繼承、多態 封裝(Encapsulation):這是定義類的 準則,單個類。根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中。 封裝的意義: 1.將屬性和方法放到一起做為一個整體,然後通過實例化對象來處理; 2.隱藏內部實現細節,只需要和對象及其屬性和方 ...
1. 封裝
面向對象三大特性:封裝、繼承、多態
封裝(Encapsulation):這是定義類的 準則,單個類。根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中。
封裝的意義:
1.將屬性和方法放到一起做為一個整體,然後通過實例化對象來處理;
2.隱藏內部實現細節,只需要和對象及其屬性和方法交互就可以了;
3.對類的屬性和方法增加 訪問許可權控制。
2. 繼承
繼承:這是設計類的 技巧,父與子。
- 主要體現是實現代碼的 重用,相同的代碼不需要重覆的編寫;
- 子類可以在父類功能上進行重寫,擴展類的功能。
在面向對象編程中,當我們已經創建了一個類,而又想再創建一個與之相似的類,比如添加幾個方法,或者修改原來的方法,這時我們不必從頭開始,可以從原來的類派生出一個新的類, 我們把原來的類稱為父類或基類,而派生出的類稱為子類或派生類,子類繼承了父類的所有數據和方法。
從技術上說, OOP里繼承最主要的用途是實現多態。對於多態而言,重要的是介面繼承性,屬性和行為是否存在繼承性,這是不一定的。事實上, 大量工程實踐表明,重度的行為繼承會導致系統過度複雜和臃腫,反而會降低靈活性。因此現在比較提倡的是基於介面的輕度繼承理念。這種模型里因為父類(介面類)完全沒有代碼,因此根本談不上什麼代碼復用。
2.1 單繼承
子類的定義如下:
class 子類名(父類名):
<statement-1>
...
<statement-N>
父類名 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函數,所以,一定要慎用這些類型檢查函數。