Python的繼承是多繼承機制,一個子類可以同時有多個直接父類;繼承可以得到父類定義的方法,子類就可以復用父類的方法。
一、繼承的語法
子類:實現繼承的類。
父類(基類、超類):被繼承的類。
子類繼承父類是在定義子類時,將多個父類放在子類之后的圓括號內,如果定義類時,未指定這個類的直接父類,則默認繼承object類,所以object類是所有類的父類(直接父類或者是間接父類)。
語法格式如下:
class SubClass (SuperClassl , SuperClass2 , ... ): # 類定義部分
使用例子:
class Animal: def dog(self): print ('我有一只狗!他叫%s'%self.dog_name) class Child: def son(self): print ('我有兩個兒子!') # 定義Myself類,繼承Animal類和Child類 class Myself(Animal,Child): def name(self): print ('我叫小明!') M = Myself() # 創建Myself對象 M.dog_name = '大黃' # 通過Myself對象添加dog_name類變量 M.dog() # 調用Myself對象的dog()方法,打印 我有一只狗!他叫大黃 M.son() # 調用Myself對象的son()方法,打印 我有兩個兒子! M.name() # 調用Myself對象的name()方法,打印 我叫小明!
在上面的例子中,定義了Animal和Child兩個父類,和一個繼承Animal類和Child類的Myself類;創建Myself對象后,可以訪問Myself對象的dog()方法和son()方法,說明Myself對象也具有dog()方法和son()方法,所以繼承可以得到父類定義的方法,通過繼承子類就可以復用父類的方法。
二、多繼承
子類會通過繼承得到所有父類的方法,那么如果多個父類中有相同的方法名,排在前面的父類同名方法會“遮蔽”排在后面的父類同名方法,例:
class Animal: def name(self): print ('我有一只狗,他叫大黃!') class Child: def name(self): print ('我有兩個兒子!') class Myself(Animal,Child): pass class Yourself(Child,Animal): pass M = Myself() M.name() # 打印 我有一只狗,他叫大黃! Y = Yourself() Y.name() # 打印 我有兩個兒子!
上面例子中,Myself類繼承了Animal類和Child類,當Myself子類對象調用name()方法時,因為Myself子類中沒有定義name()方法,Python會先在Animal父類中搜尋name()方法,一旦搜尋到就會停止繼續向下搜尋,所以運行的是Animal類中的name()方法;而Yourself子類中由於Child父類排在前面,所以運行的是Child類的name()方法。
三、重寫
子類包含與父類同名的方法稱為方法重寫(方法覆蓋),可以說是重寫父類方法,也可以說子類覆蓋父類方法。
例:
class Animal: def name(self): print ('我有一只狗,他叫大黃!') class Myself(Animal): # 重寫Animal類的name()方法 def name(self): print ('我沒有狗,我只有一只貓,他叫大白!') M = Myself() # 創建Myself對象 M.name() # 執行Myself對象的name()方法,打印 我沒有狗,我只有一只貓,他叫大白!
上面的例子中,運行M.name()時執行的不再是Animal類的fly()方法,而是Myself類的name()方法。
在子類中重寫方法之后,如果需要用到父類中被重寫的實例方法,可以通過類名調用實例方法來調用父類被重寫的方法。
例:
class Animal: def name(self): print ('我有一只狗,他叫大黃!') class Myself(Animal): # 重寫Animal類的name()方法 def name(self): print ('我還有一只貓,他叫大白!') def pet(self): Animal.name(self) # 調用被重寫的父類方法name(),使用類名.實例名調用,需要手動傳self self.name() # 執行name()方法,會調用子類重寫的name()方法 M = Myself() # 創建Myself對象 M.pet() ''' 打印 我有一只狗,他叫大黃! 我還有一只貓,他叫大白! '''
四、super函數調用父類構造方法
如果子類有多個直接的父類,那么排在前面的構造方法會遮蔽排在后面的構造方法。
例:
class Animal: def __init__(self,pet,name): self.pet = pet self.name = name def favourite_animal(self): print ('我有一只%s,他叫%s!'%(self.pet,self.name)) class Fruit: def __init__(self,kind): self.kind = kind def favourite_fruit(self): print ('我喜歡的水果是%s!'%self.kind) class Myself(Animal,Fruit): pass M = Myself('狗','大黃') # 創建Myself對象 M.favourite_animal() # 調用Myself對象的favourite_animal()方法,打印 我有一只狗,他叫大黃! M.favourite_fruit() # 調用Myself對象的favourite_fruit方法,由於未初始化Fruit對象的kind實例變量,報錯 AttributeError: 'Myself' object has no attribute 'kind'
上面例子中,Myself子類繼承了Animal父類和Fruit父類,由於Animal父類排在Fruit父類前面,所以Animal父類的構造函數遮蔽了Fruit父類的構造函數,運行M.favourite_animal()沒有任何問題,當運行M.favourite_fruit()時,由於未初始化Fruit對象的kind實例變量,所以程序會報錯。
解決以上問題,Myself應該重寫父類的構造方法,子類的構造方法可以調用父類的構造方法,有以下兩種方式:
1.使用未綁定方法,即: 父類名.__init__(self,參數1,參數2....)。
2.使用super()函數調用父類構造方法。
先查看一下super()函數的幫助信息,
>>> help(super) Help on class super in module builtins: class super(object) | super() -> same as super(__class__, <first argument>) | super(type) -> unbound super object | super(type, obj) -> bound super object; requires isinstance(obj, type) | super(type, type2) -> bound super object; requires issubclass(type2, type) | Typical use to call a cooperative superclass method: | class C(B): | def meth(self, arg): | super().meth(arg) | This works for class methods too: | class C(B): | @classmethod | def cmeth(cls, arg): | super().cmeth(arg) ...
通過幫助信息,可以看到,當調用父類的實例方法時,會自動綁定第一個參數self;當調用類方法時,會自動綁定第一個參數cls。
接下來修改上面的程序:
class Animal: def __init__(self,pet,name): self.pet = pet self.name = name def favourite_animal(self): print ('我有一只%s,他叫%s!'%(self.pet,self.name)) class Fruit: def __init__(self,kind): self.kind = kind def favourite_fruit(self): print ('我喜歡的水果是%s!'%self.kind) class Myself(Animal,Fruit): def __init__(self,pet,name,kind): Fruit.__init__(self,kind) # 通過未綁定方法調用父類構造方法 super().__init__(pet,name) # 通過super()函數調用父類構造方法 M = Myself('狗','大黃','蘋果') M.favourite_animal() # 打印 我有一只狗,他叫大黃! M.favourite_fruit() # 打印 我喜歡的水果是蘋果!