咱們常常在類的繼承當中使用super(), 來調用父類中的方法。例以下面:html
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
A:
def
func(
self
):
print
(
'OldBoy'
)
class
B(A):
def
func(
self
):
super
().func()
print
(
'LuffyCity'
)
A().func()
B().func()
|
輸出的結果爲:python
OldBoy
OldBoy
LuffyCity
A實例化的對象調用了func方法,打印輸出了 Oldboy;函數
B實例化的對象調用了本身的func方法,先調用了父類的方法打印輸出了 OldBoy ,再打印輸出 LuffyCity 。post
這樣是Python3的寫法,今天我們也只討論Python3中的super。測試
若是不使用super的話,想獲得相同的輸出截個,還能夠這樣寫B的類:ui
1
2
3
4
|
class
B(A):
def
func(
self
):
A.func(
self
)
print
(
'LuffyCity'
)
|
這樣能實現相同的效果,只不過傳了一個self參數。那爲何還要使用super()呢?spa
那我看看有這樣的一個繼承關係的類(鑽石繼承):代理
1
2
3
4
5
6
7
|
Base
/
\
/
\
A B
\
/
\
/
C
|
代碼是這樣的:code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
Base:
def
__init__(
self
):
print
(
'Base.__init__'
)
class
A(Base):
def
__init__(
self
):
Base.__init__(
self
)
print
(
'A.__init__'
)
class
B(Base):
def
__init__(
self
):
Base.__init__(
self
)
print
(
'B.__init__'
)
class
C(A, B):
def
__init__(
self
):
A.__init__(
self
)
B.__init__(
self
)
print
(
'C.__init__'
)
C()
|
輸出的結果是:htm
Base.__init__ A.__init__ Base.__init__ B.__init__ C.__init__
每一個子類都調用父類的__init__方法,想把全部的初始化操做都作一遍,可是出現了一個問題,Base類的__init__方法被調用了兩次,這是多餘的操做,也是不合理的。
那咱們改寫成使用super()的寫法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class
Base:
def
__init__(
self
):
print
(
'Base.__init__'
)
class
A(Base):
def
__init__(
self
):
super
().__init__()
print
(
'A.__init__'
)
class
B(Base):
def
__init__(
self
):
super
().__init__()
print
(
'B.__init__'
)
class
C(A, B):
def
__init__(
self
):
super
().__init__()
print
(
'C.__init__'
)
C()
|
輸出的結果是:
Base.__init__ B.__init__ A.__init__ C.__init__
這樣執行的結果就比較滿意,是大多數人想要的結果。那爲何會是這樣的結果呢?
那是由於咱們每定義一個類的時候,Python都會建立一個MRO列表,用來管理類的繼承順序。
1
2
|
print
(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
|
Python經過這個列表從左到右,查找繼承的信息。Python3中的類都是新式類,都有這個mro屬性,能看出來是廣度優先的查找原則。經典類就沒有mro屬性,但它的查找原則是深度優先。
那我回到super的問題上來,讓咱們先看看super的官方定義。
super([type[, object-or-type]])
返回一個代理對象,該對象將方法調用委託給類的父類或兄弟類。這對於訪問類中已重寫的繼承方法很是有用。搜索順序與getattr()使用的搜索順序相同,只是類型自己被跳過。
類的__mro__屬性列出了getattr()和super()使用的方法解析搜索順序。屬性是動態的,能夠在繼承層次結構更新時進行更改。
看到官方的解釋就能夠很清楚的明白,super是一個類,實例化以後獲得的是一個代理的對象,而不是獲得了父類,而且咱們使用這個代理對象來調用父類或者兄弟類的方法。
那咱們再看看super的使用方法:
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)
super至少須要一個參數,而且類型須要是類。
不傳參數的會報錯。只傳一個參數的話是一個不綁定的對象,不綁定的話也就沒什麼用了。
1
2
|
print
(
super
(C))
print
(
super
())
|
輸出結果:
RuntimeError: super(): no arguments <super: <class 'C'>, NULL>
在定義類當中能夠不寫參數,Python會自動根據狀況將兩個參數傳遞給super。
1
2
3
4
5
6
7
8
|
class
C(A, B):
def
__init__(
self
):
print
(
super
())
super
().__init__()
print
(
'C.__init__'
)
C()
|
輸出結果:
<super: <class 'C'>, <C object>> Base.__init__ B.__init__ A.__init__ C.__init__
因此咱們在類中使用super的時候參數是能夠省略的。
第三種用法, super(type, obj) 傳遞一個類和對象,獲得的是一個綁定的super對象。這還須要obj是type的實例,能夠不是直接的實例,是子類的實例也行。
1
2
3
|
a
=
A()
print
(
super
(A, a))
print
(
super
(Base, a))
|
輸出結果:
Base.__init__ A.__init__ <super: <class 'A'>, <A object>> <super: <class 'Base'>, <A object>>
第三種用法, super(type, type2)傳遞兩個類,獲得的也是一個綁定的super對象。這須要type2是type的子類。
1
2
3
|
print
(
super
(Base, A))
print
(
super
(Base, B))
print
(
super
(Base, C))
|
輸出結果:
<super: <class 'Base'>, <A object>> <super: <class 'Base'>, <B object>> <super: <class 'Base'>, <C object>>
接下來咱們就該說說查找順序了,兩個參數,是按照那個參數去計算MRO呢?
咱們將C類中的super的參數填寫上,而且實例化,看看輸出的結果。
1
2
3
4
|
class
C(A, B):
def
__init__(
self
):
super
(C,
self
).__init__()
print
(
'C.__init__'
)
|
輸出結果:
Base.__init__ B.__init__ A.__init__ C.__init__
看結果和以前super沒填參數的結果是同樣的。
那咱們將super的第一個參數改成A:
1
2
3
4
|
class
C(A, B):
def
__init__(
self
):
super
(A,
self
).__init__()
print
(
'C.__init__'
)
|
輸出結果:
Base.__init__
B.__init__
C.__init__
咦!?那A.__init__怎麼跑丟了呢?多出來了B.__init__呢?
這是應爲Python是按照第二個參數來計算MRO,此次的參數是self,也就是C的MRO。在這個順序中跳過一個參數(A)找後面一個類(B),執行他的方法。
知道這個後,輸出的結果就能夠理解了。 super(A, self).__init__() 沒有執行Base的方法,而是執行了B的方法。
那咱們接下來講說 super(type, obj) 和 super(type, type2)的區別。
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class
Base:
def
func(
self
):
return
'from Base'
class
A(Base):
def
func(
self
):
return
'from A'
class
B(Base):
def
func(
self
):
return
'from B'
class
C(A, B):
def
func(
self
):
return
'from C'
c_obj
=
C()
print
(
super
(C, C))
print
(
super
(C, c_obj))
|
輸出結果:
<super: <class 'C'>, <C object>> <super: <class 'C'>, <C object>>
兩次的打印結果如出一轍,verygood。那他們的方法是不是同樣的呢?測試一下。
1
2
|
print
(
super
(C, C).func
is
super
(C, c_obj).func)
print
(
super
(C, C).func
=
=
super
(C, c_obj).func)
|
輸出結果:
False
False
他倆的方法既不是指向同一個,值還不相等。是否是搞錯了呢?再試試下面的看看。
1
2
3
4
5
6
|
c1
=
super
(C, C)
c2
=
super
(C, C)
print
(c1
is
c2)
print
(c1
=
=
c2)
print
(c1.func
is
c2.func)
print
(c1.func
=
=
c2.func)
|
輸出結果:
False
False
True
True
c1和c2不是一個對象,可是他們的方法倒是相同的。
那 super(C, C).func 和 super(C, c_obj).func 的確是不一樣的。那打印出來看看有什麼區別:
1
2
|
print
(
super
(C, C).func)
print
(
super
(C, c_obj).func)
|
輸出結果:
<function A.func at 0x0000000009F4D6A8>
<bound method A.func of <__main__.C object at 0x00000000022A94E0>>
super的第二個參數傳遞的是類,獲得的是函數。
super的第二個參數傳遞的是對象,獲得的是綁定方法。
函數和綁定方法的區別就再也不贅述了,在這裏想獲得同樣的結果,只須要給函數傳遞一個參數,而綁定方法則不須要傳遞額外的參數了。
1
2
|
print
(
super
(C, C).func(c_obj))
print
(
super
(C, c_obj).func())
|
輸出結果:
from A from A
那我如今總結一下:
- super()使用的時候須要傳遞兩個參數,在類中能夠省略不寫,咱們使用super()來找父類或者兄弟類的方法;
- super()是根據第二個參數來計算MRO,根據順序查找第一個參數類後的方法。
- super()第二個參數是類,獲得的方法是函數,使用時要傳self參數。第二個參數是對象,獲得的是綁定方法,不須要再傳self參數。
給使用super()的一些建議:
- super()調用的方法要存在;
- 傳遞參數的時候,儘可能使用*args 與**kwargs;
- 父類中的一些特性,好比【】、重寫了__getattr__,super對象是不能使用的。
- super()第二個參數傳的是類的時候,建議調用父類的類方法和靜態方法。