微信号:Demon-5203
如有问题欢迎后台留言或加微信
加微信,可进入无广告编程学习群,免费共享学习资源
题目:Python 中如何实现多继承?以及多继承会带来什么问题?
扩展题目:你了解 Python 中的 MRO 列表吗?
上篇文章,我们遗留了一个问题,就是 Python 中的多继承问题。今天进行详细介绍。首先看下昨天的代码:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
c = C()
"""执行结果如下:enter Center Aenter Benter Baseleave Baseleave Bleave Aleave C"""
正常按照我们学 JAVA 的思路(虽然 JAVA 里并不能同时 extends 两个类,但假定它即便把它看成是一个单一继承,输出也是不一样的),代码输出应该是:
/*
enter C
enter A
enter Base
leave Base
leave A
enter B
enter Base
leave Base
leave B
leave C
*/
之所以会出现这种结果,是因为在 Python 中有一个列表叫 MRO,它的全称是 Method Resolution Order,即方法解析顺序列表。对于我们定义的每个类,它都会根据一种特定的算法(C3线性算法,这里不作深入讨论)来得到一个列表,这个列表代表了类继承的顺序。我们可以通过 mro() 方法来查看某个类的 MRO 列表:
C.mro() # 通过 类名. 来获得
c.__class__.mro() # 通过 实例对象.__class__ 来获得
c().__class__.mro() # 通过 实例对象.__class__ 来获得
"""
得到的 MRO 列表:
[multi_extends_test01.C,
multi_extends_test01.A,
multi_extends_test01.B,
multi_extends_test01.Base,
object]
"""
从结果我们可以看出,一个类的 MRO 列表会包含它所有父类的 MRO 列表,即父类的 MRO 列表其实是子类的一个真子集。而 super() 的执行就是根据这个列表而来的。下面让我们来仔细看下 super() 这个方法,它接受两个参数,第一个参数是当前子类的名称,第二个参数是 self ,它是一个固定参数,代表的是当前的实例对象。super() 执行的过程可以总结为两步:根据我们传入的实例对象,通过 instance.__class__.mro() 得到当前类的 MRO 列表
根据列表的顺序,取列表的第二个元素,返回
这样就能解释之前的代码为什么会这样输出了。关键点在于我们是通过实例对象来获取到的 MRO 列表,而在整个继承的过程中,其实实例对象是没有发生改变的。它一直是类C的实例对象。这里用代码注释的形式给大家分解下步骤:
class Base(object):
def __init__(self): # 11. 进入 Base.__init__
print("enter Base") # 12. 打印 enter Base
print("leave Base") # 13. 打印 leave Base
class A(Base):
def __init__(self): # 5. 进入 A.__init__
print("enter A") # 6. 打印 enter A
# 7. 这里将分为两步执行
# 7.1 执行 c.__class__.mro
# 7.2 返回 MRO 列表中的第二个元素,也就是 Base
super(A, self).__init__()
print("leave A") # 15. 打印 leave A
class B(Base):
def __init__(self): # 8. 进入 B.__init__
print("enter B") # 9. 打印 enter B
# 10. 这里将分为两步执行
# 10.1 执行 c.__class__.mro
# 10.2 返回 MRO 列表中的第二个元素,也就是 B
super(B, self).__init__()
print("leave B") # 14. 打印 leave A
class C(A, B):
def __init__(self): # 2. 实例 C ,会进入 __init__
print("enter C") # 3. 打印 enter C
# 4. 这里将分为两步执行
# 4.1 执行 c.__class__.mro
# 4.2 返回 MRO 列表中的第二个元素,也就是 A
super(C, self).__init__()
print("leave C") # 16. 打印 leave A
c = C() # 1. 首先代码会从这里开始执行。
"""
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
"""
其实 super() 的调用过程,有点递归的味道。最后一个问题来了,为什么 Python 要设置一个 MRO 列表来规定继承中类的执行顺序呢?这是因为像我之前学过的 JAVA 语言,它是单继承的,一个属性方法的调用是十分明确的。而在 Python 中,是多继承的,如果父类中存在同名函数的时候,是会产生二义性的,MRO 就是用来处理这种问题的。它有三个原则:子类一定在父类前面
如果存在多个父类,它会根据 MRO 列表顺序来执行
如果多个父类存在相同方法,会根据 MRO 列表选择第一个符合的类