一、原理
官方定义:
那我回到super的问题上来,让我们先看看super的官方定义。
super([type[, object-or-type]])
返回一个代理对象,该对象将方法调用委托给类的父类或兄弟类。这对于访问类中已重写的继承方法非常有用。搜索顺序与getattr()使用的搜索顺序相同,只是类型本身被跳过。
类的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。属性是动态的,可以在继承层次结构更新时进行更改。
看到官方的解释就可以很清楚的明白,super是一个类,实例化之后得到的是一个代理的对象,而不是得到了父类,并且我们使用这个代理对象来调用父类或者兄弟类的方法。
二、作用
super()的主要用法有两种
2.1 在单继承中
在单继承中,其意义就是不需要父类的名称来调用父类的函数,因此当子类改为继承其他父类的时候,不需要对子类内部的父类调用函数做任何修改就能调用新父类的方法。 比如:
class A():
def add(self):
print("A class")
class C():
def add(self):
print("C class")
class B(A): # 如果我们改为继承C()类时,调用的时候我们就要修改内部的父类调用方式
def add(self): # class B(C):
# def add(self):
A().add() # 父类名来调用父类方法 # C().add()
print("B class") # print("B class")
if __name__ == "__main__":
B().add()
2.2 多继承中
在python中,多继承是python面向对象中重中之重的重点,多继承让python程序更加的简洁,可读性更高。
而在多类继承中super()是必不可少的(多类继承是python的一大特色),super()的**__mro__变量**记录了方法执行的顺序,既一个类的所有父类的调用顺序(MRO用来保证多类继承的时候各父类被逐一调用并只被调用一次)。例如:
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
Base.__init__(self)
print('A.__init__')
class B(Base):
def __init__(self):
Base.__init__(self)
print('B.__init__')
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print('C.__init__')
C()
输出结果:
从上面的结果可以看出,执行子类中__init__时,A类和B类继承同一个Base类,因此,Base类的__init__被执行了两次,这个操作是多余的也是不合理的,所以出现了super()。
我们改成super()的写法:
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
super().__init__()
print('A.__init__')
class B(Base):
def __init__(self):
super().__init__()
print('B.__init__')
class C(A, B):
def __init__(self):
super().__init__()
print('C.__init__')
C()
输出结果:
这样的结果继承相同类只执行了一遍 ,是大多数人想要的结果。那为什么会是这样的结果呢?
那是因为我们每定义一个类的时候,Python都会创建一个MRO列表,用来管理类的继承顺序。
print(C.mro())# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
Python通过这个列表从左到右,查找继承的信息。Python3中的类都是新式类,都有这个mro属性,能看出来是广度优先的查找原则。经典类就没有mro属性,但它的查找原则是深度优先。