當前位置:
首頁 > 知識 > super 沒那麼簡單

super 沒那麼簡單

點擊上方「

Python開發

」,選擇「置頂公眾號」


關鍵時刻,第一時間送達!






說到 super, 大家可能覺得很簡單呀,不就是用來調用父類方法的嘛。如果真的這麼簡單的話也就不會有這篇文章了,且聽我細細道來。??



約定




在開始之前我們來約定一下本文所使用的 Python 版本。默認用的是 Python 3,也就是說:本文所定義的類都是新式類。如果你用到是 Python 2 的話,記得繼承 object:





# 默認, Python 3


class

A

:


    

pass


 

# Python 2


class

A

(

object

)

:


    

pass




Python 3 和 Python 2 的另一個區別是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :





# 默認,Python 3


class

B

(

A

)

:

    

def add

(

self

,

x

)

:


        

super

().

add

(

x

)

 


# Python 2


class

B

(

A

)

:


    

def add

(

self

,

x

)

:


        

super

(

B

,

self

).

add

(

x

)




所以,你如果用的是 Python 2 的話,記得將本文的 super() 替換為 suepr(Class, self) 。




如果還有其他不兼容 Python 2 的情況,我會在文中註明的。




單繼承




在單繼承中 super 就像大家所想的那樣,主要是用來調用父類的方法的。





class

A

:


    

def __init__

(

self

)

:


        

self

.

n

=

2


 


    

def add

(

self

,

m

)

:


        

print

(

"self is {0} @A.add"

.

format

(

self

))


        

self

.

n

+=

m


 


 


class

B

(

A

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

3


 


    

def add

(

self

,

m

)

:


        

print

(

"self is {0} @B.add"

.

format

(

self

))


        

super

().

add

(

m

)


        

self

.

n

+=

3




你覺得執行下面代碼後, b.n 的值是多少呢?





b

=

B

()


b

.

add

(

2

)


print

(

b

.

n

)




執行結果如下:





self

is

<

__main__

.

B

object

at

0x106c49b38

>

@

B

.

add


self

is

<

__main__

.

B

object

at

0x106c49b38

>

@

A

.

add


8




這個結果說明了兩個問題:






  1. super().add(m) 確實調用了父類 A 的 add 方法。



  2. super().add(m) 調用父類方法 def add(self, m) 時, 此時父類中 self 並不是父類的實例而是子類的實例, 所以 b.add(2) 之後的結果是 5 而不是 4 。




不知道這個結果是否和你想到一樣呢?下面我們來看一個多繼承的例子。




多繼承




這次我們再定義一個 class C,一個 class D:





class

C

(

A

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

4


 


    

def add

(

self

,

m

)

:


        

print

(

"self is {0} @C.add"

.

format

(

self

))


        

super

().

add

(

m

)


        

self

.

n

+=

4


 


 


class

D

(

B

,

C

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

5


 


    

def add

(

self

,

m

)

:


        

print

(

"self is {0} @D.add"

.

format

(

self

))


        

super

().

add

(

m

)


        

self

.

n

+=

5




下面的代碼又輸出啥呢?





d

=

D

()


d

.

add

(

2

)


print

(

d

.

n

)




這次的輸出如下:





self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

D

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

B

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

C

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

A

.

add


19




你說對了嗎?你可能會認為上面代碼的輸出類似:





self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

D

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

B

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

A

.

add


15




為什麼會跟預期的不一樣呢?下面我們將一起來看看 super 的奧秘。




super 是個類




當我們調用 super() 的時候,實際上是實例化了一個 super 類。你沒看錯, super 是個類,既不是關鍵字也不是函數等其他數據結構:





>>>

class

A

:

pass


...


>>>

s

=

super

(

A

)


>>>

type

(

s

)


<

class

"super"

>


>>>




在大多數情況下, super 包含了兩個非常重要的信息: 一個 MRO 以及 MRO 中的一個類。當以如下方式調用 super 時: :





super

(

a_type

,

obj

)






MRO 指的是 type(obj) 的 MRO, MRO 中的那個類就是 a_type , 同時 isinstance(obj, a_type) == True 。




當這樣調用時: :





super

(

type1

,

type2

)






MRO 指的是 type2 的 MRO, MRO 中的那個類就是 type1 ,同時 issubclass(type2, type1) == True 。




那麼, super() 實際上做了啥呢?簡單來說就是:提供一個 MRO 以及一個 MRO 中的類 C , super() 將返回一個從 MRO 中 C 之後的類中查找方法的對象。




也就是說,查找方式時不是像常規方法一樣從所有的 MRO 類中查找,而是從 MRO 的 tail 中查找。




舉個栗子, 有個 MRO: :





[

A

,

B

,

C

,

D

,

E

,

object

]

 




super 只會從 C 之後查找,即: 只會在 D 或 E 或 object 中查找 foo 方法。




多繼承中 super 的工作方式




再回到前面的





d

=

D

()


d

.

add

(

2

)


print

(

d

.

n

)




現在你可能已經有點眉目,為什麼輸出會是 :





self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

D

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

B

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

C

.

add


self

is

<

__main__

.

D

object

at

0x10ce10e48

>

@

A

.

add


19




了吧 ;)




下面我們來具體分析一下:






  • D 的 MRO 是: [D, B, C, A, object] 。 備註: 可以通過 D.mro() (Python 2 使用 D.__mro__ ) 來查看 D 的 MRO 信息)



  • 詳細的代碼分析如下:





class

A

:


    

def __init__

(

self

)

:


        

self

.

n

=

2


 


    

def add

(

self

,

m

)

:


        

# 第四步


        

# 來自 D.add 中的 super


        

# self == d, self.n == d.n == 5


        

print

(

"self is {0} @A.add"

.

format

(

self

))


        

self

.

n

+=

m


        

# d.n == 7


 


 


class

B

(

A

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

3


 


    

def add

(

self

,

m

)

:


        

# 第二步


        

# 來自 D.add 中的 super


        

# self == d, self.n == d.n == 5


        

print

(

"self is {0} @B.add"

.

format

(

self

))


        

# 等價於 suepr(B, self).add(m)


        

# self 的 MRO 是 [D, B, C, A, object]


        

# 從 B 之後的 [C, A, object] 中查找 add 方法


        

super

().

add

(

m

)


 


        

# 第六步


        

# d.n = 11


        

self

.

n

+=

3


        

# d.n = 14


 


class

C

(

A

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

4


 


    

def add

(

self

,

m

)

:


        

# 第三步


        

# 來自 B.add 中的 super


        

# self == d, self.n == d.n == 5


        

print

(

"self is {0} @C.add"

.

format

(

self

))


        

# 等價於 suepr(C, self).add(m)


        

# self 的 MRO 是 [D, B, C, A, object]


        

# 從 C 之後的 [A, object] 中查找 add 方法


        

super

().

add

(

m

)


 


        

# 第五步


        

# d.n = 7


        

self

.

n

+=

4


        

# d.n = 11


 


 


class

D

(

B

,

C

)

:


    

def __init__

(

self

)

:


        

self

.

n

=

5


 


    

def add

(

self

,

m

)

:


        

# 第一步


        

print

(

"self is {0} @D.add"

.

format

(

self

))


        

# 等價於 super(D, self).add(m)


        

# self 的 MRO 是 [D, B, C, A, object]


        

# 從 D 之後的 [B, C, A, object] 中查找 add 方法


        

super

().

add

(

m

)


 


        

# 第七步


        

# d.n = 14


        

self

.

n

+=

5


        

# self.n = 19


 


d

=

D

()


d

.

add

(

2

)


print

(

d

.

n

)




調用過程圖如下:





D

.

mro

()

==

[

D

,

B

,

C

,

A

,

object

]


d

=

D

()


d

.

n

==

5


d

.

add

(

2

)


 


class

D

(

B

,

C

)

:          

class

B

(

A

)

:            

class

C

(

A

)

:            

class

A

:


    

def

add

(

self

,

m

)

:      

def

add

(

self

,

m

)

:      

def

add

(

self

,

m

)

:      

def

add

(

self

,

m

)

:


        

super

().

add

(

m

)

  

1.

--->  

super

().

add

(

m

)

2.

--->  

super

().

add

(

m

)

  

3.

--->  

self

.

n

+=

m


        

self

.

n

+=

5

   <------

6.

self

.

n

+=

3

    <----

5.

self

.

n

+=

4

     <----

4.

<--|


        

(

14

+

5

=

19

)

              

(

11

+

3

=

14

)

              

(

7

+

4

=

11

)

                

(

5

+

2

=

7

)




現在你知道為什麼 d.add(2) 後 d.n 的值是 19 了吧 ;)




That』s all! 希望這篇文章能對你有所幫助 ;)




參考資料






  • Python』s super() Explained



  • 2. Built-in Functions — Python 3.5.2 documentation



  • Python』s Super Considered Harmful






  • 來源:HuangHuang




  • segmentfault.com/a/1190000008150517



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊成為Java大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

看我如何假裝是一個編程極客

TAG:Python開發 |