多繼承
多繼承以及MRO順序
一、單獨呼叫父類
# coding=utf-8
print("******多繼承使用類名.__init__ 發生的狀態******")
class Parent(object):
def __init__(self, name):
print('parent的init開始被呼叫')
self.name = name
print('parent的init結束被呼叫')
class Son1(Parent):
def __init__(self, name, age):
print('Son1的init開始被呼叫')
self.age = age
Parent.__init__(self, name)
print('Son1的init結束被呼叫')
class Son2(Parent):
def __init__(self, name, gender):
print('Son2的init開始被呼叫')
self.gender = gender
Parent.__init__(self, name)
print('Son2的init結束被呼叫')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init開始被呼叫')
Son1.__init__(self, name, age) # 單獨呼叫父類的初始化方法
Son2.__init__(self, name, gender)
print('Grandson的init結束被呼叫')
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年齡:', gs.age)
print('性別:', gs.gender)
print("******多繼承使用類名.__init__ 發生的狀態******\n\n")
執行結果
******多繼承使用類名.__init__ 發生的狀態******
Grandson的init開始被呼叫
Son1的init開始被呼叫
parent的init開始被呼叫
parent的init結束被呼叫
Son1的init結束被呼叫
Son2的init開始被呼叫
parent的init開始被呼叫
parent的init結束被呼叫
Son2的init結束被呼叫
Grandson的init結束被呼叫
姓名: grandson
年齡: 12
性別: 男
******多繼承使用類名.__init__ 發生的狀態******
二、多繼承中super呼叫有所父類的被重寫的方法
print("******多繼承使用super().__init__ 發生的狀態******")
class Parent(object):
def __init__(self, name, *args, **kwargs): # 為避免多繼承報錯,使用不定長引數,接受引數
print('parent的init開始被呼叫')
self.name = name
print('parent的init結束被呼叫')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 為避免多繼承報錯,使用不定長引數,接受引數
print('Son1的init開始被呼叫')
self.age = age
super().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長引數,接受引數
print('Son1的init結束被呼叫')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 為避免多繼承報錯,使用不定長引數,接受引數
print('Son2的init開始被呼叫')
self.gender = gender
super().__init__(name, *args, **kwargs) # 為避免多繼承報錯,使用不定長引數,接受引數
print('Son2的init結束被呼叫')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init開始被呼叫')
# 多繼承時,相對於使用類名.__init__方法,要把每個父類全部寫一遍
# 而super只用一句話,執行了全部父類的方法,這也是為何多繼承需要全部傳參的一個原因
# super(Grandson, self).__init__(name, age, gender)
super().__init__(name, age, gender)
print('Grandson的init結束被呼叫')
print(Grandson.__mro__)
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年齡:', gs.age)
print('性別:', gs.gender)
print("******多繼承使用super().__init__ 發生的狀態******\n\n")
執行結果:
******多繼承使用super().__init__ 發生的狀態******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init開始被呼叫
Son1的init開始被呼叫
Son2的init開始被呼叫
parent的init開始被呼叫
parent的init結束被呼叫
Son2的init結束被呼叫
Son1的init結束被呼叫
Grandson的init結束被呼叫
姓名: grandson
年齡: 12
性別: 男
******多繼承使用super().__init__ 發生的狀態******
注意:
- 以上2個程式碼執行的結果不同
- 如果2個子類中都繼承了父類,當在子類中通過父類名呼叫時,parent被執行了2次
- 如果2個子類中都繼承了父類,當在子類中通過super呼叫時,parent被執行了1次
三、單繼承中super
print("******單繼承使用super().__init__ 發生的狀態******")
class Parent(object):
def __init__(self, name):
print('parent的init開始被呼叫')
self.name = name
print('parent的init結束被呼叫')
class Son1(Parent):
def __init__(self, name, age):
print('Son1的init開始被呼叫')
self.age = age
super().__init__(name) # 單繼承不能提供全部引數
print('Son1的init結束被呼叫')
class Grandson(Son1):
def __init__(self, name, age, gender):
print('Grandson的init開始被呼叫')
super().__init__(name, age) # 單繼承不能提供全部引數
print('Grandson的init結束被呼叫')
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年齡:', gs.age)
#print('性別:', gs.gender)
print("******單繼承使用super().__init__ 發生的狀態******\n\n")
四、總結
- super().__init__相對於類名.init,在單繼承上用法基本無差
- 但在多繼承上有區別,super方法能保證每個父類的方法只會執行一次,而使用類名的方法會導致方法被執行多次,具體看前面的輸出結果
- 多繼承時,使用super方法,對父類的傳引數,應該是由於python中super的演算法導致的原因,必須把引數全部傳遞,否則會報錯
- 單繼承時,使用super方法,則不能全部傳遞,只能傳父類方法所需的引數,否則會報錯
- 多繼承時,相對於使用類名.__init__方法,要把每個父類全部寫一遍, 而使用super方法,只需寫一句話便執行了全部父類的方法,這也是為何多繼承需要全部傳參的一個原因
五、小試牛刀(以下為面試題)
以下的程式碼的輸出將是什麼? 說出你的答案並解釋。
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
答案, 以上程式碼的輸出是:
1 1 1
1 2 1
3 2 3
使你困惑或是驚奇的是關於最後一行的輸出是 3 2 3 而不是 3 2 1。為什麼改變了 Parent.x 的值還會改變 Child2.x 的值,但是同時 Child1.x 值卻沒有改變?
這個答案的關鍵是,在 Python 中,類變數在內部是作為字典處理的。如果一個變數的名字沒有在當前類的字典中發現,將搜尋祖先類(比如父類)直到被引用的變數名被找到(如果這個被引用的變數名既沒有在自己所在的類又沒有在祖先類中找到,會引發一個 AttributeError 異常 )。
因此,在父類中設定 x = 1 會使得類變數 x 在引用該類和其任何子類中的值為 1。這就是因為第一個 print 語句的輸出是 1 1 1。
隨後,如果任何它的子類重寫了該值(例如,我們執行語句 Child1.x = 2),然後,該值僅僅在子類中被改變。這就是為什麼第二個 print 語句的輸出是 1 2 1。
最後,如果該值在父類中被改變(例如,我們執行語句 Parent.x = 3),這個改變會影響到任何未重寫該值的子類當中的值(在這個示例中被影響的子類是 Child2)。這就是為什麼第三個 print 輸出是 3 2 3。
相關文章
- 多繼承 與 多重繼承繼承
- 繼承與多型繼承多型
- 繼承和多型繼承多型
- 多型和繼承多型繼承
- day23:單繼承&多繼承&菱形繼承&__init__魔術方法繼承
- python 基礎之繼承、重寫、多繼承Python繼承
- odoo 繼承(owl繼承、web繼承、view繼承)Odoo繼承WebView
- java繼承與多型Java繼承多型
- 繼承+多型+抽象類繼承多型抽象
- Castle 多繼承選擇AST繼承
- Scala多繼承以及AOP繼承
- JavaScript 的繼承與多型JavaScript繼承多型
- Python 繼承 和 多型Python繼承多型
- OOP的多型和繼承OOP多型繼承
- TypeScript(5)類、繼承、多型TypeScript繼承多型
- Scala 多繼承 & 依賴注入繼承依賴注入
- Javascript的繼承與多型JavaScript繼承多型
- python 多繼承詳解Python繼承
- 封裝、繼承和多型封裝繼承多型
- 原型,繼承——原型繼承原型繼承
- 菱形繼承,虛繼承繼承
- JS中的多種繼承方式JS繼承
- Java 繼承與多型實驗Java繼承多型
- C中的繼承和多型繼承多型
- C++多繼承的細節C++繼承
- C++繼承詳解:共有(public)繼承,私有(private)繼承,保護(protected)繼承C++繼承
- 三種繼承的方法:public 繼承/private繼承/protected繼承詳解及區別繼承
- Javascript繼承4:潔淨的繼承者—-原型式繼承JavaScript繼承原型
- Javascript繼承2:建立即繼承—-建構函式繼承JavaScript繼承函式
- [許可權設計]組的繼承:需要從多個父組繼承嗎?繼承
- Python中的繼承和多型Python繼承多型
- 太極1:繼承和多型2繼承多型
- C語言實現繼承多型C語言繼承多型
- php物件導向多繼承實現PHP物件繼承
- [c++] 繼承和多型整理二C++繼承多型
- Java中的類繼承與多型Java繼承多型
- 面向2-封裝、繼承、多型封裝繼承多型
- 繼承繼承