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的计算方式会在另一篇文章中讲述。

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