多重继承和方法解析顺序
python支持多继承的,下面看一个多继承的例子:
class A:
def ping(self):
print('ping:',self)
class B(A):
def pong(self):
print('pong:',self)
class C(A):
def pong(self):
print('PONG',self)
class D(B,C):
def ping(self):
super().ping()
print('post-ping:',self)
def pingpong(self):
self.ping()
super().ping()
self.pong()
super().pong()
C.pong(self)
上面的代码用下面的流程图记录为:其中左边为菱形UML图形,右边为上面代码的解析图;
在C上调用pong有两种方式:
d = D()
d.pong()
>>>pong: <__main__.D object at 0x0000019F560A11C8>
C.pong(d)#超类中的方法都可以直接调用,这时候要把实例参数作为显示参数传入;
>>>PONG <__main__.D object at 0x0000019F560A11C8>
d.pingpong()
>>> post-ping: <__main__.D object at 0x0000019F560A11C8>
ping: <__main__.D object at 0x0000019F560A11C8>
pong: <__main__.D object at 0x0000019F560A11C8>
pong: <__main__.D object at 0x0000019F560A11C8>
PONG <__main__.D object at 0x0000019F560A11C8>
Python能区分d.pong()调用的是哪个方法,是因为python会按照特定的顺序遍历继承图,这个顺序叫做方法解析顺序。调用d.pingpong()时依次执行下面步骤:
- 第一个调用self.ping(),运行的是D中的ping,输出前两行;
- 第二个调用超类的ping,跳过D中的ping,找到类A中ping
- 第三个调用的是pong,找到的是B类的pong
- 第四个调用的是超类的pong,找到的也是B类的pong
- 第五个调用C类的pong
处理多重继承
Python支持多继承,其实好多语言都不支持多继承,因为多继承容易出现很多错误的情况,因此python中也应该谨慎使用多继承。
1. 接口继承与实现继承
在使用多重继承时,一定要明确一开始为什么创建子类,主要原因可能有:
- 继承接口,创建子类型,实现‘是什么’关系;
- 继承实现,通过重用避免代码重复
通过继承重用代码是实现细节,通常可以换用组合和委托模式,而接口则是框架的支柱;
2. 使用抽象基类显式表示接口
如果类的作用是定义接口,应该明确把他定义为抽象基类,关于抽象基类与接口
前文已经介绍过,可点击查看
3. 通过混用重用代码
如果一个类的作用是为多个不相关的子类实现方法,从而使用重用,但不体现‘是什么’关系,应该把那个类明确的定义为混入类,从概念上说,混入不定义新类型,只是打包方法,便于重用,混入类绝对不能实现实例化,而且具体类不能只继承混入类,混入类应该提供某方面的特定行为,只实现少量关系;
4. 在名称中明确知名混入
Python中没有把类声明为混入的正规方式,所以一般在写的时候都建议加入…Mixin后缀。
5. 抽象基类可以作为混入,但是反过来不支持
抽象基类可以实现具体方法,因此也可以作为混入使用,不过,抽象基类定义类型,但是混入做不到。此外,抽象基类可以作为其他类的唯一基类,但是混入类不可以;
抽象基类有个局限:抽象基类中实现的方法只能与抽象基类及其超类中的方法协作;
6. 不要子类化多个具体类
具体类可以没有,或者只有一个具体超类,也就是说,具体类的超类中除了这个具体超类之外,其余的都是抽象基类或者混入;
7. 为用户提供聚合类
聚合类就是抽象类或者混入的组合;
8. 优先使用对象组合,而不是类继承
参考文献:流畅的python, Luciano Ramalho编著
来源:CSDN
作者:T-aurora
链接:https://blog.csdn.net/weixin_43419753/article/details/103757055