python多重继承中的MRO(方法解释顺序)

python允许子类继承多个基类(父类),这种特性就是通常所说的多重继承,但是在多重继承中,如何正确找到没有在当前类(或者当前子类)定义的属性。在使用多重继承的时候,有两个不同的方面要注意;1:要找到合适的属性,2:在重写一个方法的时候,如何调用对应的父类方法以发挥他们的作用,同时在子类中处理好自己的义务。

方法解释顺序(MRO: Method Resolution Order)

  1. 在Python2.2之前,算法非常简单,使用的是深度优先算法,即根据继承基类的位置信息,从左到右进行搜索,取得在子类中使用的属性,使用第一次找到的名字。
    但是在目前的版本中,这种算法就会出现问题,后续会介绍,所以新的查询方法不再是深度优先,而是广度优先算法
  2.  
    '''父类1'''
    class P1(object):
        def foo(self):
            print("called P1-foo()")
    
    
    '''父类2'''
    class P2(object):
        def foo(self):
            print("called P2-foo()")
    
        def bar(self):
            print("called P2-bar()")
    
    
    '''子类1, 从P1,P2派生'''
    class C1(P1, P2):
        pass
    
    
    '''子类2,从P1, P2 派生'''
    class C2(P1, P2):
        def bar(self):
            print("called C2-bar()")
    
    
    '''定义子孙类, 从C1,C2派生'''
    class GC(C1, C2):
        pass
    

  3. 根据上述代码,从该图中可以看到父类,子类以及子孙类的关系,P1中定义了foo(),P2中定义了foo()和bar(),C2中定义了bar()。在经典类和新式类(目前使用的版本)中行为是不同的。

经典类和新式类在MRO的不同

经典类

  1. 在经典类中,通过交互式解释器中执行上面的声明,可以验证经典类的解释顺序是深度优先,从左至右:
    gc = GC()
    gc.foo()   # GC=> C1 => P1
    输出是: called P1-foo()
    
    gc.bar()    # GC  =>  C1  => P1  => P2
    输出是: called P2-bar()
    
    

    即当调用foo()的时候,它首先在当前类(GC)中查找,如果没有找到,就向上查找最亲的父类C1, 查找未遂,继续沿着树向上查找父类P1,直到找到foo()。
    同样,对于bar(),它通过搜索GC, C1,P1,然后在P2中找到该方法,因为使用这种解释顺序的缘故,C2.bar()根本就不会被搜索到。
    所有当想调用C2的bar()方法的时候只能通过类,采用非绑定方式去调用:C2.bar(gc)

新式类(目前使用的版本)

  1. 新式类的MRO方法有所不同
  2. 即在执行gc.foo()的时候,查找顺序是:GC  =>C1 =>C2 =>P1
    z在执行gc.bar()的时候,查找顺序是: GC=> C1 => C2
    与经典类沿着树一步步的上溯不同,新式类,首先查找的是同胞兄弟,采用一种广度优先的算法,当查找foo(),它检查GC,然后是C1,C2, 然后在P1中找到,如果P1中没有,查找会到达P2, foo()的底线是,包括经典类和新式类都会在P1中找到,虽然同归,但却殊途!
    然而bar()的结果是不同的,它首先搜索C1,C2,紧接着在C2中找到了,这样就不会再继续搜索器祖父类P1和P2。 这种情况的解释方式更适合那种要查找GC更亲近的bar()方案,当然,如果要调用上一级,只要按照前述方法,使用非绑定的方式去查找就可以。
  3. 新式类也有一个__mro__属性,告诉查找的顺序

     

猜你喜欢

转载自blog.csdn.net/q1138266752/article/details/84924331