一、经典类的MRO
虽然在python3中已经不存在经典类了,但是经典类的MRO最好还是学⼀学。这是⼀种树形结构遍历的⼀个最直接的案例,在python的继承体系中,我们可以把类与类继承关系化成⼀个树形结构的图。来上代码:
class A: pass class B(A): pass class C(A): pass class D(B, C): pass class E: pass class F(D, E): pass class G(F, D): pass class H: pass class Foo(H, G): Pass
对付这样的MRO.,很简单,画图即可:
继承关系图已经有了,那如何进⾏查找呢? 记住⼀个原则,在经典类中采⽤的是深度优先遍历⽅案。什么是深度优先?就是⼀条路走到头,然后再回来,继续找下⼀个。MRO是什么呢? 很简单,记住,从头开始,从左往右,⼀条路跑到头。然后回头,继续⼀条路跑到头。就是经典类的MRO算法。
类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C.
二、新式类的MRO
python中的新式类的MRO是采⽤的C3算法来完成的。c3算法很简单,就看你的代码就够了。不需要去画图,先看代码:
class A: pass class B(A): pass class C(A): pass class D(B, C): pass class E(C, A): pass class F(D, E): pass class G(E): pass class H(G, F): pass
⾸先,我们要确定从H开始找,也就是说,创建的是H的对象。如果从H找,那找到H+H的⽗类的C3, 我们设C3算法是L(x) , 即给出x类。找到x的MRO:
L(H) = H + L(G) + L(F)
继续从代码中找G和F的⽗类往⾥⾯带
L(G) = G + L(E)
L(F) = F + L(D)+ L(E)
继续找E 和 D
L(E) = E + L(C) + L(A)
L(D) = D + L(B) + L(C)
最后就剩下⼀个A了,也就不⽤再找了。接下来,把L(A) 往⾥带,再推回去,但要记住,这⾥的 + 表⽰的是merge。merge的原则是⽤每个元组的头⼀项和后⾯元组的除头⼀项外的其他元素进⾏比较, 看是否存在。如果存在,就从下⼀个元组的头⼀项继续找,如果找不到,就拿出来。
作为merge的结果的⼀项,以此类推,直到元组之间的元素都相同,也就不⽤再找了。
L(B)) =(B,) + (A,) -> (B, A)
L(C) =(C,) + (A,) -> (C, A)
继续带:
L(E) = (E,) + (C, A) + (A) -> E, C, A
L(D) = (D,) + (B, A) + (C, A) -> D, B, A
继续带:
L(G) = (G,) + (E, C, A) -> G, E, C, A
L(F) = (F,) + (D, B, A) + (E, C, A) -> F, D, B, E, C, A
加油, 最后了:
L(H) = (H, ) + (G, E, C, A) + ( F, D, B, E, C, A) -> H, G, F, D, B, E, C, A
算完了,最终结果 HGFDBECA。那这个算完了,如何验证呢? 其实python早就给你准备好了。我们可以使⽤类名.__mro__获取到类的MRO信息。
print(H.__mro__) 结果: (<class '__main__.H'>, <class '__main__.G'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>,<class '__main__.A'>, <class 'object'>)
三. super是什么⿁?
super()可以帮我们执⾏MRO中下⼀个⽗类的⽅法,通常super()有两个使⽤的地⽅:
1. 可以访问⽗类的构造⽅法
2. 当⼦类⽅法想调⽤⽗类(MRO)中的⽅法
我们先看第⼀种:
class Foo: def __init__(self, a, b, c): self.a = a self.b = b self.c = c class Bar(Foo): def __init__(self, a, b, c, d): super().__init__(a, b, c) # 访问⽗类的构造⽅法 self.d = d b = Bar(1, 2, 3, 4) print(b.__dict__) 结果: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
这样就⽅便了⼦类,不需要写那么多了,直接⽤⽗类的构造帮我们完成⼀部分代码。
第⼆种:
class Foo: def func1(self): super().func1() # 此时找的是MRO顺序中下⼀个类的func1()⽅法 print("我的⽼家. 就住在这个屯") class Bar: def func1(self): print("你的⽼家. 不在这个屯") class Ku(Foo, Bar): def func1(self): superr().func1() # 此时super找的是Foo print("他的⽼家. 不知道在哪个屯") k = Ku() # 先看MRO . KU, FOO, BAR object k.func1() k2 = Foo() # 此时的MRO. Foo object k2.func1() # 报错
结论: 不管super()写在哪⼉,在哪⼉执⾏,⼀定先找到MRO列表。根据MRO列表的顺序往下找,否则⼀切都是错的。