python super中的mro表
发布日期:2021-06-29 16:00:27 浏览次数:2 分类:技术文章

本文共 2736 字,大约阅读时间需要 9 分钟。

子类中调用父类的方法可以使用super函数

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

结果为

A.funcNoneB.funcA.funcNone

注意super()这种写法是在python3里面的,如果python2的话你要这样用super(B, self)。当然在这里你同样可以这样去做

class B(A):    def func(self):        print('B.func')        A.func(self)

同样没有任何问题。

好,那么问题就出现了。同样可以解决问题,python中为什么要出现super()呢?

我们看这样一个问题,我们的类的继承关系是一种菱形继承

Base      /  \     /    \    A      B     \    /      \  /       C

代码是这样的

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__')print(C())

输出结果是

Base.__init__A.__init__Base.__init__B.__init__C.__init__<__main__.C object at 0x000001A493AF6358>

这显然和你想象中的输出不一样,我们看到 Base.__init__被调用了两次。当然这有没有坏处,不好说。但是有一点可以明确的是,这里的结果和大多数程序员想要的结果不一样,那么就会出现被误用的问题。当然你非这样设计也没有问题,前提是你要知道这么做的结果。

我们使用super()函数去做的话,就是这样的

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__')

结果是

Base.__init__B.__init__A.__init__C.__init__<__main__.C object at 0x000001A493ACFE10>

嗯,这个结果是我们大多数人想要的。

那么我们就要想,为什么会这样呢?

我们每定义一个类,python会创建一个MRO列表,用来管理类的继承信息(你可以把它简单的理解为一个列表)。

C.__mro__Out[14]: (__main__.C, __main__.A, __main__.B, __main__.Base, object)

python通过这个列表从左到右的顺序,查找类的继承信息。这个列表是通过C3线性算法实现的,这个算法我们不去讨论,这里我们只要知道有这个表就可以了。

接着回到super()函数问题。我们可以通过这样的形式调用super(cls, inst),这也是我们上面python2里使用的方案。这里的cls是一个类,inst可以是一个类也可以是一个对象。我们通过inst获取MRO列表。然后再这个列表中查找cls类,返回类cls后面一个类的信息。例如

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

这里我们获取到临时对象C()MRO列表

(__main__.C, __main__.A, __main__.B, __main__.Base, object)

返回类C后面类A的信息,调用super(A, self).__init__。因为self还是CMRO表不变,所以我们接着调用类A后的类Bsuper(B, self).__init__。同理接着调用super(Base, self).__init__,也就输出了Base.__init__B中的super().__init__()执行完后,会执行print('B.__init__')。然后就是你看到的打印结果。

我这里要强调的是,如果你这里A类中使用的不是super().__init__,而是使用A.__init__,那么结果会像之前那样会出人意料。因为前者在调用时,传入的instC对象,而后者传入的是A类。这两者的MRO列表不同,所以就会出现不同的结果,为了你更好的理解我这里列出ABMRO表。

A.__mro__Out[15]: (__main__.A, __main__.Base, object)B.__mro__Out[16]: (__main__.B, __main__.Base, object)

你可以借此去推导,前面出现两次Base.__init__的原因。

虽然我的这篇文章貌似把super说的很明白了,但是我强烈建议你去读一下我的这篇文章,你会发现上面很多的东西都是有待商榷的QAQ!!!

当然这是我的理解,可能有不对之处,欢迎大家指出!

转载地址:https://coordinate.blog.csdn.net/article/details/79487433 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:super(type, self)与super(type1, type2)的区别
下一篇:寻找最大排列问题

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月06日 21时15分17秒