多继承的实现
class A(object):
def out(self):
print("A类方法")
class B(object):
def out(self):
print("B类方法")
class C(A, B):
pass
c = C()
# 打印C类的调用路径顺序(注意要类名.__mro__)
print(C.__mro__)
c.out()
运行结果:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
A类方法
可以尝试一下把 C类
的继承顺序改成 B,A
class C(B, A):
pass
结果就是
(<class ‘main.C’>, <class ‘main.B’>, <class ‘object’>, <class ‘main.A’>)
B类方法
如果 C类
把 out()
方法重写那么将执行 C类的 out()
方法
class C(B, A):
def out(self):
print("C类方法")
结果如下:
(<class ‘main.C’>, <class ‘main.B’>, <class ‘object’>, <class ‘main.A’>)
C类方法
了解MRO
新式类可以直接通过
类名.__mro__
的方式获取类的MRO
,也可以通过类名.mro()
的形式,旧式类是没有 mro 属性和 mro() 方法的。
方法解析顺序 Method Resolution Order
,简称 MRO
。主要用于在多继承时判断方法,属性的调用路径。
- 在搜索方法时,是按照
mro()
输出的结果,从左到右的顺序查找的 - 如果找到,在当前类中找到方法就直接执行,不在搜索
- 没有找到,就依次查找下一个类中是否有对应的方法,找到执行,不在搜索
- 如果最后一个类,还没有找到方法,程序报错
MRO
的顺序是根据 Python中C3算法 得来的大家感兴趣可以去研究一下,这里就不在赘述了。
新式类和旧式类
在早期版本的 Python 中,所有类并没有一个共同的祖先 object
,如果定义一个类,但没有显式指定其祖先,那么就被解释为 旧式类,例如:
class oldA:
pass
class oldB:
pass
其中,oldA
和 oldB
都属于旧式类。
Python 2.x
版本中,为了向后兼容保留了旧式类。该版本中的 新式类必须 显式继承 object
或者其他新式类:
class NewA(object):
pass
class NewB(NewA):
pass
显然,以上两个类都属于 新式类。
而在 Python 3.x
版本中,不再保留旧式类的概念。因此,没有继承任何其他类的类都隐式地继承自 object
。
super()的使用
super()
函数是用于调用父类(超类)的一个方法。
super()
是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
单继承
父类名调用
"""
单继承使用父类名调用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
Parent.eat(self)
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
Parent.eat(self)
print("\tson2 --- 爱吃水果\n")
def main():
s1 = Son1()
s2 = Son2()
s1.eat()
s2.eat()
if __name__ == '__main__':
main()
运行结果
son1 --- eat()
parent --- 爱吃饭
son1 --- 爱吃蔬菜
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
使用super()
"""
单继承中super()的使用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
super().eat()
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
super().eat()
print("\tson2 --- 爱吃水果\n")
def main():
s1 = Son1()
s2 = Son2()
s1.eat()
s2.eat()
if __name__ == '__main__':
main()
运行结果:
son1 --- eat()
parent --- 爱吃饭
son1 --- 爱吃蔬菜
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
可以发现在单继承使用 父类名
和 super()
调用父类方法结果都一样。
多继承
还是上面例子,就是加一个 GrandSon
类,让它继承 Son1, Son2
。让 eat()
具备其父类的特性。
父类名调用
"""
多继承中父类名的使用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
Parent.eat(self)
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
Parent.eat(self)
print("\tson2 --- 爱吃水果\n")
class Grandson(Son1, Son2):
def eat(self):
print("grandson --- eat()")
# super().eat()
Son1.eat(self)
Son2.eat(self)
print("\tgrandson --- 爱吃零食")
def main():
# s1 = Son1()
# s2 = Son2()
# s1.eat()
# s2.eat()
g = Grandson()
g.eat()
if __name__ == '__main__':
main()
运行结果
grandson --- eat()
son1 --- eat()
parent --- 爱吃饭
son1 --- 爱吃蔬菜
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
grandson --- 爱吃零食
结果显示 Parent
的 eat()
方法调用了多次,存在重复调用。
使用super()
"""
多继承中super()的使用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
super().eat()
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
super().eat()
print("\tson2 --- 爱吃水果\n")
class Grandson(Son1, Son2):
def eat(self):
print("grandson --- eat()")
super().eat()
print("\tgrandson --- 爱吃零食")
def main():
g = Grandson()
g.eat()
if __name__ == '__main__':
main()
运行结果
grandson --- eat()
son1 --- eat()
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
son1 --- 爱吃蔬菜
grandson --- 爱吃零食
可以发现在多继承中使用 super()
没有重复调用。
假如在多继承中 Grandson
类的 eat()
方法只想复用 Parent, Son1
的 eat()
的方法,不需要 Son2
的。该如何实现呢?
父类名调用
"""
父类名的使用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
Parent.eat(self)
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
Parent.eat(self)
print("\tson2 --- 爱吃水果\n")
class Grandson(Son1, Son2):
def eat(self):
print("grandson --- eat()")
Son1.eat(self)
# Son2.eat(self)
print("\tgrandson --- 爱吃零食")
def main():
print(Grandson.mro())
g = Grandson()
g.eat()
if __name__ == '__main__':
main()
结果如下
[<class '__main__.Grandson'>,
<class '__main__.Son1'>,
<class '__main__.Son2'>,
<class '__main__.Parent'>,
<class 'object'>
]
grandson --- eat()
son1 --- eat()
parent --- 爱吃饭
son1 --- 爱吃蔬菜
grandson --- 爱吃零食
[Finished in 0.1s]
super()调用
"""
super()的使用
"""
class Parent(object):
def eat(self):
print("\tparent --- 爱吃饭")
class Son1(Parent):
def eat(self):
print("son1 --- eat()")
super().eat()
print("\tson1 --- 爱吃蔬菜\n")
class Son2(Parent):
def eat(self):
print("son2 --- eat()")
super().eat()
print("\tson2 --- 爱吃水果\n")
class Grandson(Son1, Son2):
def eat(self):
print("grandson --- eat()")
super(Son1, self).eat()
# Son1.eat(self)
# Son2.eat(self)
print("\tgrandson --- 爱吃零食")
def main():
print(Grandson.mro())
g = Grandson()
g.eat()
if __name__ == '__main__':
main()
结果如下
[<class '__main__.Grandson'>,
<class '__main__.Son1'>,
<class '__main__.Son2'>,
<class '__main__.Parent'>,
<class 'object'>
]
grandson --- eat()
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
grandson --- 爱吃零食
[Finished in 0.1s]
然而却发现 super(Son1, self).eat()
调用的是 Son2
的 eat()
方法。
是因为 MRO
的原因,当调用 super(Son1,self).eat()
时 ,会拿 Son1
去 Grandson
的MRO方法解析顺序表 中寻找,找到然后 super()
调用则是列表中下一个,这里是 Son2
,然后 Son2.eat()
中使用了 super().eat()
,此时是拿其本身 Son2
在 Son2
的MRO方法解析顺序表 中寻找,然后 super().eat()
调用,则是列表的下一个 Parent.eat()
。
因此在 Grandson
中 super(Son1, self).eat()
调用的是 Son2.eat()
。
假如 super(Son2, self).eat()
调用的则是 Parent.eat()
。结果如下
[<class '__main__.Grandson'>,
<class '__main__.Son1'>,
<class '__main__.Son2'>,
<class '__main__.Parent'>,
<class 'object'>
]
grandson --- eat()
parent --- 爱吃饭
grandson --- 爱吃零食
因此只能使用 父类名 的形式调用 Son1.eat()
只继承 Son1
的特性。如果不清楚 super()
调用的谁,打印其
类名.mro(),对照 MRO方法解析顺序表,就一目了然。
总结
- 方法解析顺序
Method Resolution Order
,简称MRO
。主要用于在多继承时判断方法,属性的调用路径 - 旧式类,没有共同的 object祖先且没有显式的指定其祖先。
- 新式类,在
Python 2.x
版本中显式继承object
或者其他新式类,Python3.x
中则是隐式继承object
super().method()
相对于类名.method(self)
,在 单继承 上用法基本无差- 在 多继承 上有区别,
super()
方法 能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果