属性查找
单继承下的属性查找:
有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找……
class Foo:
... def f1(self):
... print('Foo.f1')
... def f2(self):
... print('Foo.f2')
... self.f1()
...
class Bar(Foo):
... def f1(self):
... print('Foo.f1')
...
b=Bar()
b.f2()
>>> Foo.f2
>>> Foo.f1
Foo.f2():对象自己本身的__dict__里没有Foo.f2(),所以要去父类中找。但是传的参数中self为obj本身。所以再次运行self.f1()的时候其实是运行 obj.f1(),这时再去__dict__,找到执行Bar(Foo)中的f1。
如果就像让在调用Foo的f2时调用Foo的f1,则需要这样写:
class Foo:
def __f1(self): # _Foo__f1
print('Foo.f1')
def f2(self):
print('Foo.f2')
# Foo.f1(self)
self.__f1()
class Bar(Foo):
def __f1(self): # _Bar__f1
print('Bar.f1')
b=Bar()
b.f2()
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
Foo.f1(self)
class Bar(Foo):
def f1(self):
print('Bar.f1')
b=Bar()
b.f2()
b.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Foo.f1。
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B,C):
pass
obj = D()
obj.test() # 结果为:from B
类以及该类的对象访问属性都是参照该类的mro列表
在Python3下:
D.mro() # 只要定义了类.mor()就会被定义出来;新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
结果是: [<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
所以obj.test()的查找顺序是,先从对象obj本身的属性里找方法test,没有找到,则参照属性查找的发起者(即obj)所处类D的MRO列表来依次检索,首先在类D中未找到,然后再B中找到方法test
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,
PS: super()函数,一旦被调用则是在mro()产生的列表里去找上一个,然后再进行.之后的调用。
总结:
类的相关属性的查找(类名.属性,该类的对象.属性),都是参照该类的mro
PS:Python2和3在非菱形继承下查找顺序一样,新式类(即Python中默认继承object类)object类永远放最后一个查找;在Python2中非新式类就没有object在列表中。
菱形问题:
上面的图中,EFD最终继承一个非object类时出现的问题。
https://www.cnblogs.com/gandoufu/p/9634914.html
总结:
多继承要用,但是:
1、继承结构尽量不要过于复杂
2、要在多继承的背景下满足继承的什么“是”什么的关系 -> 推荐使用mixins原则