天天看點

python學習第十五天 -面向對象之繼承和多态

大家都知道面向對象的三大特性:封裝,繼承,多态。封裝特性在上一章節已經講解過。這一章節主要講解繼承和多态。

繼承:

當定義一個類的時候,可以從現有的類進行繼承。那麼新定義的類可以稱為子類,被繼承的現有的類稱為基類,父類或者超類。

上一章節大家應該有所注意以下代碼:

>>> class Animal(object):
    pass      

object類是python所有類的基類,Animal類是object類的子類。Animal類繼承于object類。

繼承到底有什麼好處?舉個例子:

>>> class Animal(object):
    def call(self):
        print('Animal is calling!')        
>>> class dog(Animal):
    pass
>>> dog = dog()
>>> dog.call()
Animal is calling!      

dog類繼承了Animal類,當聲明了一個dog執行個體對象後,可以直接調用父類Animal的call方法。

這就是繼承的好處,能夠直接繼承來自父類的屬性和方法,然後直接調用。這樣就精簡了代碼,提高了代碼的複用性。

如果父類在執行個體化對象的時候,要預設傳入name參數的話,子類dog繼承的時,應該怎麼辦?

例子:

>>> class Animal(object):
    def __init__(self,name):
        self.name = name
    def call(self):
        print ('Animal is calling!')

        
>>> class dog(Animal):
    def __init__(self,name,birth):
        super(dog,self).__init__(name)//大家注意這句代碼
        self.birth = birth
    def call(self):
        print ('dog is calling!')

        
>>> dog = dog('wangcai','2016-8-5')
>>> print dog.name
wangcai
>>> print dog.birth
2016-8-5      

函數super(dog, self)将傳回目前類繼承的父類,即Animal,然後調用__init__()方法,注意self參數已在super()中傳入,在__init__()中将隐式傳遞,不需要寫出(也不能寫).

大家發現,dog子類也定義了一個call方法。那麼執行個體化dog對象,dog.call()會發生什麼?

多态

>>> dog.call()
dog is calling!      

當子類和父類都存在相同的

call()

方法時,我們說,子類的

call()

覆寫了父類的

call()

,在代碼運作的時候,總是會調用子類的

call()

。類似于C++的虛函數或者java的抽象方法。

在了解多态之前,要判斷一個對象/變量是否屬于某一個類/類型,使用isinstance方法。

>>> class Animal(object):
    def call(self):
        print('Animal is calling!')

        
>>> class dog(Animal):
    def call(self):
        print('dog is calling!')

        
>>> d =dog()
>>> a =Animal()
>>> isinstance(d,Animal)
True
>>> isinstance(a,dog)
False      

看來,d不僅僅是dog,而且還是animal。而a 雖然是animal,但是不一定是dog,也有可能是cat。是以isinstance(a,dog)傳回為false。

是以在繼承關系中,如果一個執行個體的資料類型是某個子類,那麼它的資料類型也可以被看做是父類。是以isinstance(d,Animal)傳回為True。

有個上述的了解,那麼我們再舉個例子。

>>> def lound_call(animal):
    animal.call()
>>> lound_call(dog())
dog is calling!
>>> lound_call(Animal())
Animal is calling!      

新增一個Animal子類cat。

>>> class Cat(Animal):
    def call(self):
        print('cat is calling!')
>>> lound_call(Cat())
cat is calling!      

你會發現,新增一個

Animal

的子類,不必對lound_call

()

做任何修改,實際上,任何依賴

Animal

作為參數的函數或者方法都可以不加修改地正常運作,原因就在于多态。

多态的好處就是,當我們需要傳入

Dog

Cat

……時,我們隻需要接收

Animal

類型就可以了,因為

Dog

Cat

……都是

Animal

類型,然後,按照

Animal

類型進行操作即可。由于

Animal

類型有

call()

方法,是以,傳入的任意類型,隻要是

Animal

類或者子類,就會自動調用實際類型的

call()

方法,這就是多态的意思:

對于一個變量,隻需要知道它是

Animal

類型,無需确切地知道它的子類型,就可以放心地調用

call()

方法,而具體調用的

call()

方法是作用在

Animal

Dog

Cat

對象上,由運作時該對象的确切類型決定,這就是多态真正的威力:調用方隻管調用,不管細節,而當我們新增一種

Animal

的子類時,隻要確定

call()

方法編寫正确,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則:

對擴充開放:允許新增

Animal

子類;

對修改封閉:不需要修改依賴

Animal

類型的

lound_call()

等函數。

小結:這一章節主要講解了python面向對象的屬性繼承和多态。