python 支持多继承:
任何实现多重继承的语言都要处理潜在的命名冲突,这种冲突由不相关的祖先类实现同名 方法引起。这种冲突称为“菱形问题”,如图 12-1 和示例 12-4 所示。
示例代码:
"""
python 中多继承:
"""
import pysnooper
class A:
@pysnooper.snoop()
def ping(self):
print('A_ping:', self)
class B(A):
@pysnooper.snoop()
def pong(self):
print('B_pong:', self)
class C(A):
@pysnooper.snoop()
def pong(self):
print('C_PONG:', self)
class D(B, C):
def ping(self):
# 这里的super() 是调用B类
super().ping()
print('D_post-ping:', self)
def pingpong(self):
# 这里自身方法的调用,解析顺序和刚才一样
self.ping()
# 这里的super() 是调用B类
super().ping()
# 先查找自身,在查找B,在查找C,最后才会查找A
self.pong()
# 这里的super() 是调用B类
super().pong()
# 这里是调用C类方法
C.pong(self)
if __name__ == "__main__":
d = D()
# 先查找自身,在查找B,在查找C,最后才会查找A
d.pong() # 依次放开,通过 pysnooper 可以看到执行顺序
# d.ping() # 依次放开,通过 pysnooper 可以看到执行顺序
# d.pingpong() # 依次放开,通过 pysnooper 可以看到执行顺序
1,放开 d.pong()
结果:
2,放开 d.ping()
, 注释 d.pong()
,结果:
3,放开 d.pingpong()
, 注释 d.pong()
和 d.ping()
结果:
当然也可以像下图的调用方式(超类中的方法都可以直接调用,此时要把实例作为显式参数传入。):
Python 能区分 d.pong() 调用的是哪个方法,是因为 Python 会按照特定的顺序遍历继承图。 这个顺序叫方法解析顺序(Method Resolution Order,MRO)。类都有一个名为 __mro__
的 属性,它的值是一个元组,按照方法解析顺序列出各个超类,从当前类一直向上,直到 object 类。D 类的 __mro__
属性如下:
若想把方法调用委托给超类,推荐的方式是使用内置的 super()
函数。在 Python 3 中,这 种方式变得更容易了,如示例 中 D
类的 pingpong
方法所示。然而,有时可能需要绕 过方法解析顺序,直接调用某个超类的方法——这样做有时更方便。如图:
注意,直接在类上调用实例方法时,必须显式传入 self
参数,因为这样访问的是未绑定方法(unbound method)。
然而,使用 super()
最安全,也不易过时。调用框架或不受自己控制的类层次结构中的方 法时,尤其适合使用 super()
。使用 super()
调用方法时,会遵守方法解析顺序。
方法解析顺序不仅考虑继承图,还考虑子类声明中列出超类的顺序。也就是说,如果在 上面的代码中把 D
类声明为 class D(C, B):
,那么 D 类的 __mro__
属 性就会不一样:先搜索 C
类,再搜索 B
类。
查看几个类的 __mro__
属性