面向對象的編程帶來的主要好處之一是代碼的重用,實現各種重用的方法之一是通過繼承機制。繼承完全可以理解成類之間的父類和子類型關系。
繼承概念:繼承是類與類的一種關系,是一種子類與父類的關系,即爸爸與兒子,爸爸生個兒子,兒子繼承爸爸的屬性和方法。
如貓類,貓是動物;貓類繼承於動物類,動物類為父類也是所有動物的基類;貓類是動物類的子類,也是動物類的派生類。
Python有單繼承與多繼承。單繼承即子類繼承於一個類,多繼承即子類繼承於多個類,多繼承會比較少遇到,本章節主要講單繼承。
什么時候使用繼承:假如我需要定義幾個類,而類與類之間有一些公共的屬性和方法,這時我就可以把相同的屬性和方法作為基類的成員,而特殊的方法及屬性則在本類中定義。這樣子類只需要繼承基類(父類),子類就可以訪問到基類(父類)的屬性和方法了,它提高了代碼的可擴展性和重用行。
如:貓,狗 都屬於動物,它們行為相似性高,都會叫,會吃,會喝,會拉,會撒嬌。
一、Python類的繼承
如下定義一個動物類animal為基類,他的基本兩個實例屬性name和age\一個方法call
class Animal(object): # python3中所有類都可以繼承於object基類 def __init__(self, name, age): self.name = name self.age = age def call(self): print(self.name, '會叫') ###### # 現在我們需要定義一個Cat 貓類繼承於Animal,貓類比動物類多一個sex屬性。 ###### class Cat(Animal): def __init__(self,name,age,sex): super(Cat, self).__init__(name,age) # 不要忘記從Animal類引入屬性 self.sex=sex if __name__ == '__main__': # 單模塊被引用時下面代碼不會受影響,用於調試 c = Cat('喵喵', 2, '男') # Cat繼承了父類Animal的屬性 c.call() # 輸出 喵喵 會叫 ,Cat繼承了父類Animal的方法
注意:一定要用 super(Cat, self).__init__(name,age) 去初始化父類,否則,繼承自 Animal的 Cat子類將沒有 name和age兩個屬性。
函數super(Cat, self)將返回當前類繼承的父類,即 Animal,然后調用__init__()方法,注意self參數已在super()中傳入,在__init__()中將隱式傳遞,不能再寫出self
二、Python 對子類方法的重構
上面例子中 Animal 的子類 Cat 繼承了父類的屬性和方法,但是我們貓類 Cat 有自己的叫聲 '喵喵' ,這時我們可以對父類的 Call() 方法進行重構。如下:
class Cat(Animal): def __init__(self, name, age, sex): super(Cat, self).__init__(name,age) self.sex = sex def call(self): print(self.name,'會“喵喵”叫') if __name__ == '__main__': c = Cat('喵喵', 2, '男') c.call() # 輸出:喵喵 會“喵喵”叫
類方法的調用順序,當我們在子類中重構父類的方法后,Cat子類的實例先會在自己的類 Cat 中查找該方法,當找不到該方法時才會去父類 Animal 中查找對應的方法。
三、Python中子類與父類的關系
class Animal(object): pass class Cat(Animal): pass A= Animal() C = Cat()
子類與父類的關系是 “is” 的關系,如上 Cat 繼承於 Animal 類,我們可以說:
“A”是 Animal 類的實例,但,“A”不是 Cat 類的實例。
“C”是 Animal 類的實例,“C”也是 Cat 類的實例。
判斷對象之間的關系,我們可以通過 isinstance (變量,類型) 來進行判斷:
print('"A" IS Animal?', isinstance(A, Animal)) print('"A" IS Cat?', isinstance(A, Cat)) print('"C" IS Animal?', isinstance(C, Animal)) print('"C" IS Cat?', isinstance(C, Cat))
輸出結果:
"A" IS Animal? True
"A" IS Cat? False
"C" IS Animal? True
"C" IS Cat? True
拓展:isinstance() 判斷變量類型
函數 isinstance() 不止可以用在我們自定義的類,也可以判斷一個變量的類型,如判斷數據類型是否為 int、str、list、dict 等。
print(isinstance(100, int)) print(isinstance('100', int)) print(isinstance(100, str)) print(isinstance('100', str))
輸出:
True
False
False
True
四、python 中多態
類具有繼承關系,並且子類類型可以向上轉型看做父類類型,如果我們從 Animal 派生出 Cat和 Dog,並都寫了一個 call() 方法,如下示例:
class Animal(object): def __init__(self, name, age): self.name = name self.age = age def call(self): print(self.name, '會叫') class Cat(Animal): def __init__(self, name, age, sex): super(Cat, self).__init__(name, age) self.sex = sex def call(self): print(self.name, '會“喵喵”叫') class Dog(Animal): def __init__(self, name, age, sex): super(Dog, self).__init__(name, age) self.sex = sex def call(self): print(self.name, '會“汪汪”叫')
我們定義一個 do 函數,接收一個變量 ‘all’,如下:
def do(all): all.call() A = Animal('小黑',4) C = Cat('喵喵', 2, '男') D = Dog('旺財', 5, '女') for x in (A,C,D): do(x) 小黑 會叫 喵喵 會“喵喵”叫 旺財 會“汪汪”叫
小知識:多態
這種行為稱為多態。也就是說,方法調用將作用在 all 的實際類型上。C 是 Cat 類型,它實際上擁有自己的 call() 方法以及從 Animal 繼承的 call 方法,但調用 C .call() 總是先查找它自身的定義,如果沒有定義,則順着繼承鏈向上查找,直到在某個父類中找到為止。
傳遞給函數 do(all) 的參數 all 不一定是 Animal 或 Animal 的子類型。任何數據類型的實例都可以,只要它有一個 call() 的方法即可。其他類不繼承於 Animal,具備 call 方法也可以使用 do 函數。這就是動態語言,動態語言調用實例方法,不檢查類型,只要方法存在,參數正確,就可以調用。
五、Python類繼承 注意事項:
- 在繼承中基類的構造方法(__init__()方法)不會被自動調用,它需要在其派生類的構造方法中親自專門調用。
- 在調用基類的方法時,需要加上基類的類名前綴,且需要帶上self參數變量。而在類中調用普通函數時並不需要帶上self參數
- Python 總是首先查找對應類的方法,如果它不能在派生類中找到對應的方法,它才開始到基類中逐個查找。(先在本類中查找調用的方法,找不到才去基類中找)