虽然 Python 不是一个专门的面向对象的语言,但是 Python 仍然有着优秀的封装,继承,多态的机制。其中继承尤其是多重继承是在复杂项目中经常用到的。但是多重继承在使用的过程中有很多需要注意的地方,以至于很多开发人员都会尽量避免使用多重继承。

Python 支持多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法。如下代码:

class A:
    def test_a(self):
        print('Test function of A')

class B:
    def test_b(self):
        print('Test function of B')

class C(A, B):
    pass

# __base__ 这个属性可以用来获取当前类的所有父类
print(C.__base__)
# printout: (<class '__main__.A'>, <class '__main__.B'>)

c = C()
c.test_a()
# printout: Test function of A
c.test_b()
# printout: Test function of B

在多重继承中,如果多个父类中有同名方法,则会优先第一个父类[在上面代码中,如果有同名方法,就会优先使用 A 的]。

考虑下图的复杂情况,D继承了A,B,C,同时,C还继承了AB。此时,如果要调用D.test(),则现在D本身寻找,如果没有则在第一个父类[也就是 A]中寻找,如果没有则会去A的第一个父类[也就是 Object]中寻找。如果还是没有,则去第二个父类B中寻找,然后再去B的父类[Object]中寻找,这里因为刚才已经找过一个Object这里就会跳过。依然没有找到test方法的话,最后会寻找C,及其父类AB,但是因为AB都找过了,所以仍然会跳过。可以看出多重继承,调用方法的过程会相对复杂

Python Multi-Inheritance

在具体开发中,多重继承会让代码变得复杂。如果必须使用,建议 Mixin 方式

在设计类的继承关系时,通常,主线都是单一继承下来的。但是,如果需要混入额外的功能,通过多重继承可以实现。比如,Dog类可以继承是Animal,但是有一个“特殊”的能力就是Runnable。此时,Runnable就是一个额外混入的功能,毕竟不只是 Dog 才能 Runnable,但是也不是所有的 Animal 都可以 Runnable。

所以为了更好地看出继承关系,可以把 Runnable 改为 RunnableMixin。类似的,你还可以定义出肉食动物 CarnivorousMixin 和草食动物 HerbivoresMixin,让某个动物同时拥有好几个 Mixin。这样Dog这个类就可以写成:

class Dog(Animal, RunnableMixin, CarnivorousMixin):
    pass

这样一来,即便在复杂项目中不得不使用多重继承时,也不需要设计复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。