1. 程式人生 > 實用技巧 >面向物件(2)__繼承多型1

面向物件(2)__繼承多型1

1.簡單的繼承

class Animal():
    def __init__(self):
        pass
    def run(self):
        print('animal is running')    
class Dog(Animal):
    pass
if __name__ =="__main__":
    dog=Dog()
    dog.run()#輸出 animal is running

2.重寫父類方法

class Animal():
    def __init__(self):
        pass
    def run(self):
        
print('animal is running') class Dog(Animal): def run(self): print('dog is running') if __name__ =="__main__": dog=Dog() dog.run()#輸出 dog is running

3.多型

要理解什麼是多型,我們首先要對資料型別再作一點說明。當我們定義一個 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

同時,子類例項也屬於父類的型別如:

>>> isinstance(c, Animal)
True

但是,父類例項不屬於子類的型別如:

>>> isinstance(b, Dog)
False

多型demo:

class Animal():
    def __init__(self):
        pass
    def run(self):
        print('animal is running')
class Dog(Animal):
    def run(self):
        print('dog is running')

#定義一個函式
def run_twice(animal):
    animal.run()
    animal.run()
if __name__ =="__main__":
    run_twice(Animal()) #傳入一個Animal例項  'animal is running'輸出兩次
    run_twice(Dog())#傳入一個Animal子類 Dog例項  'dog is running'輸出兩次

理解:

接下來可以定義Cat等等各種繼承於Animal,不必對 run_twice()做任何修改,任何依賴 Animal 作為引數的函式或者方法都可以不加修改地正常執行,原因就在於多型多型的好處就是,當我們需要傳入 Dog、Cat、Tortoise……時,我們只需要接收 Animal 型別就可以了,因為 Dog、Cat、Tortoise……都是 Animal型別,然後,按照 Animal 型別進行操作即可。由於 Animal 型別有 run()方法,因此,傳入的任意型別,只要是 Animal 類或者子類,就會自動呼叫實際型別的 run()方法,這就是多型的意思:對於一個變數,我們只需要知道它是 Animal 型別,無需確切地知道它的子型別,就可以放心地呼叫 run()方法,而具體呼叫的 run()方法是作用在 Animal、Dog、Cat 還是 Tortoise 物件上,由執行時該物件的確切型別決定,這就是多型真正的威力:呼叫方只管呼叫,不管細節,而當我們新增一種 Animal 的子類時,只要確保 run()方法編寫正確,不用管原來的程式碼是如何呼叫的。

這就是著名的“開閉”原則:

對擴充套件開放:允許新增 Animal 子類;
對修改封閉:不需要修改依賴 Animal 型別的 run_twice()等函式。

4.動態語言 鴨子型別

動態語言的“鴨子型別”,它並不要求嚴格的繼承體系,一個物件只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。
在靜態語言(如java)中,run_twice(animal)方法中,如果需要傳入 Animal 型別的例項,則傳入的物件必須是 Animal 型別或者它的子類,否則,將無法呼叫 run()方法。
但是在動態語言(如python)中,只要我們傳入的型別有run()方法,就可以正常呼叫
舉例:我們可以定義一個類

class Animal():
    def __init__(self):
        pass
    def run(self):
        print('animal is running')
class Dog(Animal):
    def run(self):
        print('dog is running')

#定義一個函式
def run_twice(animal):
    animal.run()
    animal.run()
if __name__ =="__main__":
    run_twice(Animal()) #傳入一個Animal例項  'animal is running'輸出兩次
    run_twice(Dog())#傳入一個Animal子類 Dog例項  'dog is running'輸出兩次

Python 的“file-like object“就是一種鴨子型別。對真正的檔案物件,它有一個 read()方法,返回其內容。但是,許多物件,只要有 read()方法,都被視為“file-like object“。許多函式接收的引數就是“file-like object“,你不一定要傳入真正的檔案物件,完全可以傳入任何實現了 read()方法的物件