浅谈Python多继承与(Pthony2、3)搜索顺序的变化
一、Pyhton的多继承
说到面向对象的三大特性:封装、继承、多态。今天我们聊到的是Pyhton中的多继承,与Java语言不同的是,Pyhton它是支持多继承的,怎么说呢?(一个儿子可以有多个爹,哈哈。)
接下来先简单的代码实现下:
class A:
def test(self):
print('AAAAAA')
class B:
def test1(self):
print('BBBBBB')
class C(A,B): #C同时继承了A,B两个父类
pass
c = C()
c.test()
c.test1()
执行结果如下:
AAAAAA
BBBBBB
可以看到,C同时继承了A,B两个父类,也可以调用到父类的方法。这就是python的多继承。
二、多继承的搜索顺序
那既然可以多继承,我们就要考虑到,比如在两个父类中,有同名方法,那么在我子类中会去怎么调用呢?哪个优先级高呢?
class Base: #爷爷类(针对于D)
def test(self):
print('Base')
class A(Base): #爸爸类1(针对于D)
def test(self):
print('AAAAAA')
class B(Base): #爸爸类2(针对于D)
def test(self):
print('BBBBBB')
class C(Base): #爸爸类3(针对于D)
def test(self):
print('CCCCCC')
class D(A,B,C):
pass
d =D()
d.test()
执行结果如下:
AAAAAA
可以看到,它先搜索到的是父类A,也就是继承的第一个类。由此可以看出它的搜索顺序是从左至右。在这里,我们可以引入一个模块inspect,它的作用是可以帮助我们看到我们多继承的顺序,接下来去看看这个模块。
import inspect
print(inspect.getmro(D))
接上面的代码我们导入inspect模块,调用它的getmro(cls)方法,传入我们的子类D。得到的结果如下:
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Base'>, <class 'object'>)
它返回了一个元组,里面是D在多继承之后它的搜索顺序。D-->A-->B-->C-->object
查看inspect的底层我们可以看到,它实际上是用了魔术方法__mro__,那其实我们也可以直接通过print(D.__mro__)得到同样的结果。
三、深度优先与广度优先
接下来我们继续探究,会提到两个概念:深度优先,广度优先以及它在pthon2和python3的区别。
从Python2.2开始,Python 引入了 new style class(新式类),它和经典类区别就在于定义类的时候形式上多了个(object),具体区别后面我会专门写出来,在这主要理解多继承。
#经典类
class P1: #新式类class P1(object):
def foo(self):
print('P1-foo')
class P2:
def foo(self):
print('P2-foo')
def bar(self):
print('P2-bar')
class C1(P1,P2):
pass
class C2(P1,P2):
def bar(self):
print('C2-bar')
class D(C1,C2):
pass
d = D()
print(D.__mro__)
d.foo()
d.bar()
D作为最新类来讲,P1,P2作为爷爷类,C1,C2作为父类。那么它在经典类(pyhton2)中的搜索顺序就是:
D-->C1-->P1-->P2-->C2 我们称之为深度优先。
我的理解是它找完自己之后从左至右开始找第一个父类C1,再去从左至右深入找C1的父类P1,P2。然后返回来再找第二个父类C2。
那么它在新式类(python3)中的搜索顺序就是:
D-->C1-->C2-->P1-->P2 我们称之为广度优先。
我的理解是它找完自己之后先从左至右找自己的父类C1,C2。再去从左至右找C1的父类P1,P2。
那按照python3的执行结果如下:
当然如果有python2版本的小伙伴可以去看看它的执行顺序的变化。在3版本上运行我们看到的顺序就是广度优先。
在面试中算问得比较多的一个问题,希望我的解答能带给你帮助。