# MRO和super
# 类的多重继承-菱形继承-C3(类广度)
Python中的MRO(方法解析顺序)。在没有多重继承的情况下,向对象发出一个消息,如果对象没有对应的方法,那么向上(父类)搜索的顺序是非常清晰的。如果向上追溯到object类(所有类的父类)都没有找到对应的方法,那么将会引发AttributeError异常。
但是当出现多重继承尤其是菱形继承的时候,向上追溯到底应该找到那个方法就得确定MRO。Python 3中的类以及Python 2中的新式类使用C3算法 (opens new window)来确定MRO,它是一种类似于广度优先搜索的方法;Python 2中的旧式类(经典类)使用深度优先搜索来确定MRO。在搞不清楚MRO的情况下,可以使用类的mro方法或__mro__属性来获得类的MRO列表。
新式类可以直接通过
类名.__mro__ 的方式获取类的MRO,也可以通过类名.mro() 的形式,旧式类是没有 mro 属性和 mro() 方法的。
方法解析顺序 Method Resolution Order,简称 MRO。主要用于在多继承时判断方法,属性的调用路径。
在搜索方法时,是按照
mro() 输出的结果,从左到右的顺序查找如果找到,在当前类中找到方法就直接执行,不在搜索
没有找到,就依次查找下一个类中是否有对应的方法,找到执行,不在搜索
如果最后一个类,还没有找到方法,程序报错
可以通过
super方法来调用父类的函数,其被设计用来解决多重继承问题单继承中与父类名调用无区别
多重继承中能保证父类名只调用一次
父类名调用则还是会调用多次
在python2.x中,函数super()需要两个实参:子类名和对象self。为帮助python将父类和子类联系起来,这两个参数必不可少。
在python3.x中,函数super()可以不携带实参-解释器会自动将当前类和self传入。如果携带了参数,例如
super(B, self)中self指向B则是指找到self的mro表中B的下一个类
# encoding: utf-8
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 eat2(self):
return super(Son1, self).eat()
if __name__ == '__main__':
g = Grandson()
print(Grandson.__mro__) # (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <type 'object'>)
g.eat()
"""
grandson --- eat()
son1 --- eat()
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
son1 --- 爱吃蔬菜
grandson --- 爱吃零食
"""
# g.eat2()
"""
son2 --- eat()
parent --- 爱吃饭
son2 --- 爱吃水果
"""
# super
注意到super是有四种用法的。
super()与super(__class__,self)等价。
super(type, instance)用于调用instance继承链上位于type后的下一个类的同名方法。
super(type, type2)用于调用type2类在type的继承链上的下一个类(MRO中的下一个类)的同名方法。在使用super(type, type2)时,首先需要确保type2是type的子类,否则会抛出TypeError异常。
class super(object)
| super() -> same as super(__class__, <first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)
| Typical use to call a cooperative superclass method:
| class C(B):
| def meth(self, arg):
| super().meth(arg)
| This works for class methods too:
| class C(B):
| @classmethod
| def cmeth(cls, arg):
| super().cmeth(arg)
# MRO例题
# 题目:阅读下面的代码说出运行结果。
class A:
def who(self):
print('A', end='')
class B(A):
def who(self):
super(B, self).who()
print('B', end='')
class C(A):
def who(self):
super(C, self).who()
print('C', end='')
class D(B, C):
def who(self):
super(D, self).who()
print('D', end='')
item = D()
item.who()
点评:这道题考查到了两个知识点:
- Python中的MRO(方法解析顺序)。在没有多重继承的情况下,向对象发出一个消息,如果对象没有对应的方法,那么向上(父类)搜索的顺序是非常清晰的。如果向上追溯到
object类(所有类的父类)都没有找到对应的方法,那么将会引发AttributeError异常。但是有多重继承尤其是出现菱形继承(钻石继承)的时候,向上追溯到底应该找到那个方法就得确定MRO。Python 3中的类以及Python 2中的新式类使用C3算法 (opens new window)来确定MRO,它是一种类似于广度优先搜索的方法;Python 2中的旧式类(经典类)使用深度优先搜索来确定MRO。在搞不清楚MRO的情况下,可以使用类的mro方法或__mro__属性来获得类的MRO列表。-
super()函数的使用。在使用super函数时,可以通过super(类型, 对象)来指定对哪个对象以哪个类为起点向上搜索父类方法。所以上面B类代码中的super(B, self).who()表示以B类为起点,向上搜索self(D类对象)的who方法,所以会找到C类中的who方法,因为D类对象的MRO列表是D --> B --> C --> A --> object。
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
ACBD
← 对象和比较及深浅拷贝 mix-in →