鱼C论坛

 找回密码
 立即注册
查看: 2199|回复: 2

[已解决]同样的mro顺序,不同的输出结果

[复制链接]
发表于 2023-6-10 22:35:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

没有用super()的代码
class A:
    def __init__(self):
        print("哈喽,我是A~")

class B1(A):
    def __init__(self):
       A.__init__(self)
       print("哈喽,我是B1~")

class B2(A):
    def __init__(self):
        A.__init__(self)
        print("哈喽,我是B2~")

class C(B1, B2):
    def __init__(self):
        B1.__init__(self)
        B2.__init__(self)
        print("哈喽,我是C~")

c = C()
print(C.__mro__)

哈喽,我是A~
哈喽,我是B1~
哈喽,我是A~
哈喽,我是B2~
哈喽,我是C~
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)


用了super()的代码
class A:
    def __init__(self):
        print("哈喽,我是A~")

class B1(A):
    def __init__(self):
       super().__init__()
       print("哈喽,我是B1~")

class B2(A):
    def __init__(self):
        super().__init__()
        print("哈喽,我是B2~")

class C(B1, B2):
    def __init__(self):
        super().__init__()
        print("哈喽,我是C~")

c = C()
print(C.__mro__)

哈喽,我是A~
哈喽,我是B2~
哈喽,我是B1~
哈喽,我是C~
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)


请问以上两段代码,mro的顺序是一样的,为什么用了super()的代码和未用的输入结果的顺序不同啊。一个是先输出“我是B2”,一个是先输出“我是B1”,谢谢指点。




最佳答案
2023-6-10 22:39:02
这两段代码中的差异在于直接调用基类方法和使用 super() 的方式。当直接调用基类的 __init__ 方法时,会直接执行该方法,而不考虑方法解析顺序(MRO)中的其他类。然而, super() 的工作方式就有点不同。

Python的 super() 函数是用来调用父类(超类)的一个方法。它返回的是一个临时对象,这个对象就是父类或者是兄弟类。它并不是简单地找到其父类然后执行其父类的方法,而是按照MRO(Method Resolution Order,方法解析顺序)去查找下一个需要执行的类。

在使用了 super() 的代码中,Python的MRO遵循C3线性化或者叫做C3超类线性化。在类的继承关系中,先从自身开始查找,然后是父类,如果有多个父类,会先选择在类定义中排在前面的父类。

在你的例子中,MRO的顺序是C -> B1 -> B2 -> A。当在类C的 __init__ 方法中调用 super().__init__() 时,会根据MRO寻找到下一个类,也就是B1。然后在B1的 __init__ 中再次调用 super().__init__() ,继续按照MRO找到下一个类B2,同样地,在B2中再次调用 super().__init__() ,最后找到类A。

这就是为什么当使用 super() 时,输出的结果会按照MRO的顺序出现。相比之下,直接调用基类的 __init__ 方法不会考虑MRO,只会直接执行指定基类的方法,这就是为什么在未使用 super() 的代码中,类A的 __init__ 方法被调用了两次,因为它被B1和B2的 __init__ 方法分别调用了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-6-10 22:39:02 | 显示全部楼层    本楼为最佳答案   
这两段代码中的差异在于直接调用基类方法和使用 super() 的方式。当直接调用基类的 __init__ 方法时,会直接执行该方法,而不考虑方法解析顺序(MRO)中的其他类。然而, super() 的工作方式就有点不同。

Python的 super() 函数是用来调用父类(超类)的一个方法。它返回的是一个临时对象,这个对象就是父类或者是兄弟类。它并不是简单地找到其父类然后执行其父类的方法,而是按照MRO(Method Resolution Order,方法解析顺序)去查找下一个需要执行的类。

在使用了 super() 的代码中,Python的MRO遵循C3线性化或者叫做C3超类线性化。在类的继承关系中,先从自身开始查找,然后是父类,如果有多个父类,会先选择在类定义中排在前面的父类。

在你的例子中,MRO的顺序是C -> B1 -> B2 -> A。当在类C的 __init__ 方法中调用 super().__init__() 时,会根据MRO寻找到下一个类,也就是B1。然后在B1的 __init__ 中再次调用 super().__init__() ,继续按照MRO找到下一个类B2,同样地,在B2中再次调用 super().__init__() ,最后找到类A。

这就是为什么当使用 super() 时,输出的结果会按照MRO的顺序出现。相比之下,直接调用基类的 __init__ 方法不会考虑MRO,只会直接执行指定基类的方法,这就是为什么在未使用 super() 的代码中,类A的 __init__ 方法被调用了两次,因为它被B1和B2的 __init__ 方法分别调用了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-6-10 23:00:05 | 显示全部楼层
非常感谢您的指点,分析清晰,一语中的,向您学习!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-26 21:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表