方法解析顺序MRO
在Python的多继承里面,不熟悉MRO可能会在逐级继承时调用哪一个父类的方法产生困惑,本篇文章就针对python多继承时的MRO展开解析。Python的多继承使用的是C3算法来计算MRO,
具体的C3算法可搜索其他文章,本文章就针对C3算法的三个性质展开说明。
C3算法性质一:local precedence order,局部优先顺序。假设A,B类如下:
class A:
def say(self):
print("say A")
class B:
def say(self):
print("say B")
当进行多继承的时候,M就会优先使用写在前面的class的方法,实例如下:
class M(B, A):
pass
m = M()
print(M.mro())
m.say() # Say B
class M(A, B):
pass
m = M()
print(M.mro())
m.say() # Say A
这就是局部优先顺序的性质,当创建出新的子类并且继承自M时,那么M的所有子类也要遵循局部优先顺序的特性。
C3算法性质二:monotonicity, 单调性。简述其意思:任何一个class所使用的方法必须来自于自直接父类的方法,不能调过父类继续向上找。举例说明:A,B类还是如上图,
class C(A):
pass
class D(C, B):
pass
class M(D):
pass
m = M()
print(M.mro())
m.say() # say A
print的结果还是A,M只继承D,按照单调性,M的直接方法只能来自于父类iD,D的方法只能来自于父类C,B,按照局部优先顺序,D会先搜索父类C的say方法,最后发现父类C的say方法是Say A,所以M的say方法就是say A。
C3算法性质三:extended precedence graph。拿下图举例,该性质研究的是scrolling-mixin和editing-mixin对于他们子类的继承优先顺序。首先需要找到scrolling-mixin和editing-mixin的最小公共子类,很明显edtitable-scrollable-pane就是他们的最小公共子类。
对于最小公共子类来说,只要scrolling-mixin或者scrolling-mixin的子类排在editing-mixin或者editing-mixin子类之前,那么scrolling-mixin的mro位置就要比editing-mixin靠前,对于editing-mixin同理。
所以在python多继承时就要MRO就要满足上述性质,不满足则会抛出异常。如下:A,B还是一样,M同时继承C,D,根据局部优先顺序,C要求其子类先A后B,D要求其子类先B后A,M同时继承C,D,但C,D的局部优先顺序不一致,导致M无法计算出线性的MRO抛出异常。
class C(A, B):
pass
class D(B, A):
pass
class M(C, D):
pass
m = M()
print(M.mro())
m.say()
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B
Super的用法
其实Super是一个类,日常见到比较多的情况下如下:super()的形式,括号里的参数缺省。其实缺省的两个参数分别是:Person, self。
class Animal:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
class Person(Animal):
def __init__(self, name: str, age: int, gender: str):
self.gender = gender
super().__init__(name, age)
super(param1, param2)中的param1参数类型必须是类,param2的参数类型可以是一个类或者一个对象。param2决定了使用怎样的 mro。param1决定了从 mro 哪个 class 后面的 class 开始寻找,并将函数绑定到第二个参数上。
以下图为例,Man类初始化函数下的super(Person, self),self指的就是Man类对象,计算其MRO为[Man, Person, Animal, Object],Person后的第一个类为Animal,所以super构造的是Animal类的对象,并将其绑定到了man类对象下。
class Animal:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
class Person(Animal):
def __init__(self, name: str, age: int, gender: str):
self.gender = gender
super(Person, self).__init__(name, age)
class Man(Person):
def __init__(self, name: str, age: int, phone: str):
self.phone = phone
super(Person, self).__init__(name, age)