我们先来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13

class A:
name = "envse"

def __init__(self):
self.name = "job"


a = A()
print(a.name)

# 输出结果:
job

上面的运行结果是没有任何疑问的,那么我们为啥要专门说明一下这个类和实例的查找顺序呢?那是因为在多继承的条件之下,我们的继承关系就比较复杂了,一眼直接看出来就比较难了。

MRO算法

MRO就是method resolution order,主要用于在多继承时判断所调用属性的路径(来自于哪个类)

经典类

在Python2.2之前,我们Python里面的类称为经典类,经典类是一种没有继承的类,实例类型都是type类型,如果经典类被作为父类,子类调用父类的构造函数时会出错。这时MRO的方法为DFS(深度优先搜索(子节点顺序:从左到右))

在上面的图里面就是:A–>B–>D–>C–>E。也就是说会先往一条路深了查找,如果找不到就走另一条路。按照这种情况其实是非常合理的,B里面找不到我们就去查找它的父类D,但是我们来看下面的情况:

按照之前的分析就应该是:A–>B–>D–>C,可是假如我们C里面重写了D的方法,我们还按照之前的深度优先算法的话,那么C有可能就起不到我们想要的作用了。所以深度优先对于菱形继承就有问题了。

新式类

在Python2.2之后,新式类就诞生了。新式类的每个类都继承于一个基类,可以是自定义类或者其它类,默认会继承object类。子类可以调用父类的构造函数。然后就引入了(广度优先搜索(子节点顺序:从左到右))

按照广度优先搜索的原则就应该是:A–>B–>C–>D,这样其实很合理的,把与B同级的C也搜索完,我们才去搜索D。同样还是有一个问题:

按照广度优先搜索的原则就应该是:A–>B–>C–>D–E,可是假如我们BCD三者都有一个共同的方法,我们先去B里面查找,发现没有就去C里面查找,可是因为B已经继承了D,所以应该把BD当做一个整体,这样C就会覆盖掉D里面的方法,所以广度优先搜索就显得不够用了,所以说这个广度优先搜索在Python2.3之后就被取代了。

这样,从Python2.3开始一直到现在,Python采用的就是C3算法了,关于C3算法网上有很多文章,这里贴几篇文章:你真的理解Python中MRO算法吗?python多重继承C3算法

我们先来解决这个菱形继承模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class D:
pass


class B(D):
pass


class C(D):
pass


class A(B, C):
pass


print(A.__mro__) # 输出A的继承关系

# 输出结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)

这实际上就是采用了广度优先的算法,我们接下来再来解决正常继承模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

class D:
pass


class E:
pass


class B(D):
pass


class C(E):
pass


class A(B, C):
pass


print(A.__mro__) # 输出A的继承关系

# 输出结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)

这实际上就是采用了深度优先的算法。

[ - ]参考文章:python类学习以及mro–多继承属性查找机制.

Python学习笔记(四) 多重继承及内部算法解析.