python:關於super

python2和python3中使用super的區別

super只能用在新式類中。在python3中的所有類都是新式類,而在python2繼承object的纔是新式類。

# python3 
class A:
    pass
    
# python2
class A(object):
    pass

python3可以直接使用super().xxx代替super(Class, self).xxx

# python3
class B(A):
    def foo(self):
        super().foo()

# python2
def B(A):
    def foo(self):
        super(B, self).foo()

單繼承

在單繼承中的super是主要用來調用父類的方法的。

class A:
    def foo(self):
        print('A.foo', self)

class B(A):
    def foo(self):
        super().foo()
        print('B.foo', self)

執行以下代碼

b = B()
b.foo()

執行結果如下

A.foo <__main__.B object at 0x000001548A54D828>
B.foo <__main__.B object at 0x000001548A54D828>

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

  1. super().foo()調用了父類A的foo方法
  2. super().foo()調用父類方法時,父類方法中接收到的self對象不是父類實例而是子類的實例

多繼承

再定義兩個類,看看多繼承會有什麼不同

class C(A):
    def foo(self):
        super().foo()
        print('C.foo', self)

class D(B, C):
    def foo(self):
        super().foo()
        print('D.foo', self)

ABCD四個類的繼承關係是一個典型的“菱形繼承”,如下:

      A
     /  \
    /    \
   B      C
    \    /
     \  /
       D

執行以下代碼

d = D()
d.foo()

輸出如下

A.foo <__main__.D object at 0x000001DDD2AADB70>
C.foo <__main__.D object at 0x000001DDD2AADB70>
B.foo <__main__.D object at 0x000001DDD2AADB70>
D.foo <__main__.D object at 0x000001DDD2AADB70>

從結果可以看出,C並不是B的父類,但是B類中的super().foo()卻調用了C類的foo方法,這是爲什麼呢?

在多繼承中使用super並不像單繼承那麼簡單,會涉及到方法查找順序(MRO)。

super如何查找方法

super實質上是一個類,而不是函數或者其他數據結構。

當調用super()的時候,實際上是創建了一個super類的實例。

>>> class A:
...     pass
...
>>> s = super(A)
>>> type(s)
<class 'super'>
>>>

在大多數情況下,super對象包含了兩個非常重要的信息,MRO(Method Resolution Order)列表和MRO中的一個類。

  1. 當使用super(type1, obj)方式調用super時,MRO列表指的是type(obj)的MRO列表,type1是MRO中的一個類,同時要滿足isinstance(obj, type1) is True。
  2. 當使用super(type1, type2)方式調用super時,MRO指的是type2的MRO列表,type1是MRO中的一個類,同時issubclass(type2, type1) is True。

super會從MRO列表中type1之後的類中查找,查找要執行的方法對象後再返回這個對象。

比如說有個MRO列表:

[A, B, C, D, object]

下面的調用:

super(B, A).foo()

super只會從B之後查找,即只會在C、D、object中查找foo方法。

多繼承中super的工作方式

回到前面的多繼承的例子

d = D()
d.foo()

D的MRO是:[D, B, C, A, object]。(可以通過D.mro()來查看D的MRO信息)

代碼分析如下:

class A:
    def foo(self):
        # 第四步
        print('A.foo', self)


class B(A):
    def foo(self):
        # 第二步
        # 等價於super(B, self).foo()
        # self的MRO是[D, B, C, A, object]
        # 從B之後的[C, A, object]中查找foo方法
        super().foo()
        # 第六步
        print('B.foo', self)


class C(A):
    def foo(self):
        # 第三步
        # 等價於super(C, self).foo()
        # self的MRO是[D, B, C, A, object]
        # 從C之後的[A, object]中查找foo方法
        super().foo()
        # 第五步
        print('C.foo', self)


class D(B, C):
    def foo(self):
        # 第一步
        # 等價於super(D, self).foo()
        # self的MRO是[D, B, C, A, object]
        # 從D之後的[B, C, A, object]中查找foo方法
        super().foo()
        # 第七步
        print('D.foo', self)


d = D()
d.foo()

關於super的內容就這麼多。在多繼承模式中mro的計算方式會在另一篇文章中講述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章