天天看点

理解Python中的Super

Python中可以直接通过调用父类名调用父类方法,在多重继承中,使用super()是一个很好的习惯。

super的本质返回的是MRO的下一个类

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]
           

两个参数 cls 和 inst 分别做了两件事: 

  1. inst 负责生成 MRO 的 list 
  2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1] 

这两件事才是 super 的实质,一定要记住! 

MRO 全称 Method Resolution Order,它代表了类继承的顺序。

很重要的一句话:在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。

放几个例子

例1:

class A:
 def print(self):
  print('A')
 
class B:
 def print(self):
  print('B')
 
class C(A):
 def print(self):
  print('C')
  super().print()
 
C().print()
# C
# A
           

例2:

class A(object):
    def __init__(self):
        print('A')

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

class C(object):
    def __init__(self):
        print('C')

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

class E(B, D):
    def __init__(self):
        super(E, self).__init__()
        super(A, self).__init__()
        print('E')

e = E()
           
理解Python中的Super

分析:

print(e.__class__.__mro__)得到MRO序列(<class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>)

super(E, self).__init__()在上述MRO中找E后续的类为B,根据顺序找到B类继承类A,最后输出A、B

super(A, self).__init__()在MRO中找A后续的类为D,根据顺序找到D类继承类C,最后输出C、D

例3:(来自参考文献2)

class A:
    def __init__(self):
        print("A", end=" ")
        super().__init__()


class E:
    def __init__(self):
        print("E", end=" ")
        super().__init__()


class B(E):
    def __init__(self):
        print("B", end=" ")
        super().__init__()


class D:
    def __init__(self):
        print("D", end=" ")
        super().__init__()


class C(A, B, D):
    def __init__(self):
        print("C", end=" ")
        A.__init__(self)    # A B E D
        D.__init__(self)    # D
        B.__init__(self)    # B E D


print("MRO:", [x.__name__ for x in C.__mro__])
print(C())
           
MRO: ['C', 'A', 'B', 'E', 'D', 'object']
C A B E D D B E D <__main__.C object at 0x1040340b8>
           

分析:

当执行A.__init__(self)这行代码的时候,self指向了c实例对象,然后打印出A,当调用super().__init__()这行代码的时候,实际相当于super(A, self).__init__()的调用, super(A, self)的会在self.mro中查找排在A后边的类,这样就实现了继承链的调用了。

例4:(来自参考文献1)

class root(object):
    def __init__(self):
        print('this is root')
class B(root):
    def __init__(self):
        print('enter B')
        super().__init__()
        print('leave B')
class C(root):
    def __init__(self):
        print('enter C')
        super().__init__()
        print('leave C')
class D(B, C):
    pass

d = D()
print(d.__class__.__mro__)
           
enter B
enter C
this is root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.root'>, <class 'object'>)
           

分析:

super().__init__()这句的self还是D,所以从MRO序列中一次读取。之所以B的init会被调用是因为D中没有定义__init__,所以会在MRO中的下一个类中找到定义的__init__。

所以,使用super,如果你要改变子类继承的父类(由A改为B),在例子1中只需要修改一行代码(class C(A): -> class C(B))即可,而不需要在class C的大量代码中去查找、修改基类名,另外一方面代码的可移植性和重用性也更高。

参考文献:

1. 理解 Python super(里面提到了比较好的一篇文章)

2. python中super()的一些用法