Pyhton 类继承之 super 的用法和 MRO 方法解析顺序 1

本文是介绍 Python 中继承或多继承时 super 的工作原理和方法解析顺序(Method Resolution Order, MRO), 从根本上理解super(cls, inst) 背后到底是什么意思和 MRO。在阅读相关代码时,一定要切记当前 self 是谁,类型是什么,继承关系是什么,MRO是什么, 这样才能准确把握代码的调用流程。

在 demo 之前,先说两个知识点:

  1. MRO 方法解析顺序
    对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表: MyClass.mro() 或 MyClass.__mro__或 obj.__class__.mro()

    那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,总的来说,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
    a. 子类永远在父类前面
    b. 如果有多个父类,会根据它们在列表中的顺序被检查
    c. 如果对下一个类存在两个合法的选择,选择第一个父类

  2. super 的工作原理

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

    其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
    a. 获取 inst 的 MRO 列表
    b. 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]

    所以 super 其实和父类没有实质性的关联。即当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。

  3. 拓展:
    根据 super 的工作原理,如果你在调用 super() 的地方,修改第一个参数,则调用路径就会发生改变,哈哈

示例代码:

类的继承关系如图:
	   Base
		/ \
	   /   \
	  A     B
	   \   /
	    \ /
	  MyClass

class Base(object):
    def __init__(self):
        print 'enter class base'
        print 'In class Base: self = {0}'.format(self)
        print 'leave class base'

class A(Base):
    def __init__(self):
        print 'enter class A'
        print 'In class A: self = {0}'.format(self)
        # 重点关注:super(A, self).__init__() 到底执行的是什么
        print '---111---'
        super(A, self).__init__()
        print '---222---'
        print 'leave class A'

class B(Base):
    def __init__(self):
        print 'enter class B'
        print 'In class B: self = {0}'.format(self)
        super(B, self).__init__()
        print 'leave class B'

class MyClass(A, B):
    def __init__(self):
        print 'enter class MyClass'
        print 'In class MyClass: self = {0}'.format(self)
        print 'MRO = {0}'.format(MyClass.mro())
        super(MyClass, self).__init__()
        print 'leave class MyClass'

obj = MyClass()

The output is
	enter class MyClass
	In class MyClass: self = <__main__.MyClass object at 0x0000000002699080>
	MRO = [<class '__main__.MyClass'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <type 'object'>]
	enter class A
	In class A: self = <__main__.MyClass object at 0x0000000002699080>
	---111---
	enter class B
	In class B: self = <__main__.MyClass object at 0x0000000002699080>
	enter class base
	In class Base: self = <__main__.MyClass object at 0x0000000002699080>
	leave class base
	leave class B
	---222---
	leave class A
	leave class MyClass		

流程说明:

首先看类 MyClass 的 __init__ 方法:

super(MyClass, self).__init__()

这里的 self 是当前 MyClass 的实例,self.__class__.mro() 结果是:

[<class ‘__main__.MyClass’>, <class ‘__main__.A’>, <class ‘__main__.B’>, <class ‘__main__.Base’>, <type ‘object’>]

可以看到,MyClass 的下一个类是 A,于是,跳到了 A 的 __init__,这时会打印出 enter class A,并执行下面一行代码:

super(A, self).__init__()

注意,这里的 self 也是当前 MyClass 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 __init__,这时会打印出 enter classs B,而不是 enter class Base。

整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法
参考: https://www.cnblogs.com/silencestorm/p/8404046.html

发布了44 篇原创文章 · 获赞 0 · 访问量 3959

猜你喜欢

转载自blog.csdn.net/cpxsxn/article/details/99656157