標籤:lis 設定 ref 開放 建構函式 ima 方便 父類 false
本知識點參考廖雪峰的Python課程 [https://www.liaoxuefeng.com]感謝北京圖靈知非的免費課程 [http://www.tulingxueyuan.com/]
2018/6/26 星期二 11:15:57
繼承和多態
- 繼承就是一個類可以獲得另外一個類中的成員屬性和成員方法
- 作用: 減少代碼,增加代碼的複用功能, 同時可以設定類與類直接的關係
- 繼承與被繼承的概念:
- 被繼承的類叫父類,也叫基類,也叫超類
- 用於繼承的類,叫子類,也叫衍生類別
- 繼承與被繼承一定存在一個 is-a 關係
繼承的文法:
class Animal(object): def run(self): print("Animal is runnung...") # Animal 繼承於object,第一個類都繼承於object,不管你寫不寫`object` class Dog(Animal): pass # Dog繼承於Animal class Cat(Animal): pass # Cat繼承於Animal
- 對於
Dog來說,Animal就是它的父類,對於Animal來說,Dog就是它的子類
繼承的好處就在於子類獲得了父類的全部功能。Dog也有了Animal的Run() 方法
>>> dog = Dog()# 子類一旦繼承父類,則可以使用父類中除私人成員外的所有內容>>> dog.runAnimal is runnung...
我們對上面代碼再次進行改進,給Dog和Cat添加一些方法
class Dog(Animal): def run(self): print(‘Dog is running...‘) class Cat(Animal): def run(self): print(‘Cat is running...‘)
運行:
>>> dog = Dog() >>> cat = Cat() >>> dog.run() >>> cat.run() Dog is running... Cat is running...
我們可以看到這裡,子類和父類都存在run方法時,子類的run()覆蓋了父類的run().在代碼啟動並執行時候總會調用子類的run().這樣我們就獲得了繼承的另外一個好處,多態。
多態
要理解什麼是多態,我們首先要對資料類型再作一點說明。當我們定義一個class的時候,我們實際上就定義了一種資料類型。我們定義的資料類型和Python內建的資料類型,比如str、list、dict沒什麼兩樣:
a = list() # a是list類型 b = Animal() # b是Animal類型 c = Dog() # c是Dog類型
判斷一個變數是否是某個類型可以用isinstance()判斷:
>>> isinstance(a, list) True >>> isinstance(b, Animal) True >>> isinstance(c, Dog) True
- 看來
a,b,c都對於著list、Animal、Dog這三種類型
但是:
>>> isinstance(c,Animal) True
- 看來c不僅僅是
Dog,c還是Animal!
不過仔細想想,這是有道理的,因為Dog是從Animal繼承下來的,當我們建立了一個Dog的執行個體c時,我們認為c的資料類型是Dog沒錯,但c同時也是Animal也沒錯,Dog本來就是Animal的一種!
所以,在繼承關係中,如果一個執行個體的資料類型是某個子類,那它的資料類型也可以被看做是父類。但是,反過來就不行:
>>> b = Animal() >>> isinstance(b, Dog) False
要理解多態的好處,我們編寫一個函數
def run_twice(animal): animal.run() animal.run()
當我們傳入Animal的執行個體時,run_twice()就列印出:
>>> def run_twice(Animal()): Animal is running.. Animal is running..
當我們傳入Dog的執行個體時,run_twice()就列印出:
>>> def run_twice(Dog()): Dog is running.. Dog is running..
當我們傳入Cat的執行個體時,run_twice()就列印出:
>>> def run_twice(Cat()): Cat is running.. Cat is running..
如果我們再定義一個Tortoise類型,也從Animal派生:
class Tortoise(Animal): def run(self): print(‘Tortoise is running slowly...‘)
當我們調用run_twice()時,傳入Tortoise的執行個體:
>>> run_twice(Tortoise()) Tortoise is running slowly... Tortoise is running slowly...
對於一個變數,我們只需要知道它是Animal類型,無需確切地知道它的子類型,就可以放心地調用run()方法,而具體調用的run()方法是作用在Animal、Dog、Cat還是Tortoise對象上,由運行時該對象的確切類型決定,這就是多態真正的威力:調用方只管調用,不管細節,而當我們新增一種Animal的子類時,只要確保run()方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則:
- 對擴充開放:允許新增Animal子類;
- 對修改封閉:不需要修改依賴Animal類型的run_twice()等函數。
添加:
- super()
子類如果想擴充父類的方法,可以在定義新方法的同時訪問父類成員來進行代碼重用,可以使用 [父類名.父類成員] 的格式來調用父類成員,也可以使用super().父類成員的格式來調用。
class Animal(object): def run(self): print("Animal is runnung...") class Dog(Animal): def run(self): print(‘Dog is running...‘) class Cat(Animal): def run(self): super().run() print(‘Cat is running...‘)
- 繼承變數函數的尋找順序問題
- 優先尋找自己的變數
- 沒有則尋找父類
- 建構函式如果本類中沒有定義,則自動尋找調用父類建構函式
- 如果本類有定義,則不在繼續向上尋找
- 建構函式
- 是一類特殊的函數,在類進行執行個體化之前進行調用
- 如果定義了建構函式,則執行個體化時使用建構函式,不尋找父類建構函式
- 如果沒定義,則自動尋找父類建構函式
- 如果子類沒定義,父類的建構函式帶參數,則構造對象時的參數應該按父類參數構造
多繼承於重繼承
- 繼承物件導向編程的一個重要方式,通過繼承,子類就可以擴充父類的功能
- 如果我們把我們能想到的動物進行分類
- Dog - 狗狗;
- Bat - 蝙蝠;
- Parrot - 鸚鵡;
- Ostrich - 鴕鳥
- 我們能飛的,能跑的,能遊的,哺乳動物等分類,就太麻煩了,類的數量會指數級增長
我們正確的做法是採用多重繼承,首先主要層次任然安裝哺乳動物,和鳥類分:
class Animal(object): pass# 大類:class Mammal(Animal): passclass Bird(Animal): pass# 各種動物:class Dog(Mammal): passclass Bat(Mammal): passclass Parrot(Bird): passclass Ostrich(Bird): pass
然後,我們在給動物加上Runnable和Flyable的功能,只需要先定義好Runnable和Flyable的類:
class Runnable(object): def run(self): print(‘Running...‘)class Flyable(object): def fly(self): print(‘Flying...‘)
-對於需要Runnable功能的動物,就多繼承一個Runnable,例如Dog:
class Dog(Mammal, Runnable): pass
對於需要Flyable功能的動物,就多繼承一個Flyable,例如Bat:
class Bat(Mammal, Flyable): pass
通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。
- Mixin
- Mixin設計模式
- 主要採用多繼承方式對類的功能進行擴充
- Mixin概念
- MRO and Mixin
- Mixin模式
- Mixin MRO
- MRO
- 我們使用多繼承文法來實現Minxin
- 使用Mixin實現多繼承的時候非常小心
- 首先他必須表示某一單一功能,而不是某個物品
- 職責必須單一,如果由多個功能,則寫多個Mixin
- Mixin不能依賴於子類的實現
- 子類及時沒有繼承這個Mixin類, 也能照樣工作,只是缺少了某個功能
- 優點
- 使用Mixin可以在不對類進行任何修改的情況下,擴充功能
- 可以方便的組織和維護不同功能組件的劃分
- 可以根據需要任意調整功能類的組合
- 可以避免建立很多新的類,導致類的繼承混亂
Python物件導向編程-02