本文通过示例介绍了 super 函数的使用。 分享给大家,供大家参考,如下:
这是一个高大上的函数。 如果你在13手册中使用太多,可能会显得你是23333高手。但实际上,它仍然很重要。 简单来说,super函数就是调用下一个父类(超类)并返回父类实例的方法。 这里下一个概念是指后面介绍的MRO表。
help介绍如下:
super(type, obj) -> 绑定 super ; (对象,类型)
超级(类型)->超级
super(type, type2) -> 绑定 super ; (类型2,类型)
用于调用:
C(B)类:
def meth(self, arg):
超级(C,自我).meth(arg)
可见super有3种用法。 第一个参数始终是调用父类的类,第二个参数是可选的(返回未绑定的父类对象),也可以是该类的实例对象或子类。 最后,它返回父类的实例(绑定或未绑定)。 中,super函数多了一个直接super()的用法,相当于super(type,第一个参数)。
另外,在py2中,super只支持新类(new-style class,继承自)。
为什么称父类为?
类继承时,如果重新定义一个方法,该方法会覆盖父类中对应的同名方法。 通过调用父类实例,可以在子类中同时实现父类的功能。 例如:
# Should be new-class based on object in python2. class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" super(B, self).__init__() print "leave B" >>> b = B() enter B enter A leave A leave B
实例的初始化功能可以通过调用super获取父类实例来实现。 这在实践中太常见了(因为必须继承父类的功能,并且必须添加新的功能)。
直接使用父类调用的区别
其实上面的super函数方法也可以写成如下:
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B"
直接使用父类名来调用父类的方法其实也是可行的。 至少在上面的例子中,它们现在是一样的。 该方法也是旧式类中调用父类的唯一方法(旧式类没有super)。
通过父类名调用方法是非常常见和直观的。 不过它的效果和super还是有区别的。 例如:
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B" class C(A): def __init__(self): print "enter C" A.__init__(self) print "leave C" class D(B,C): def __init__(self): print "enter D" B.__init__(self) C.__init__(self) print "leave D" >>> d=D() enter D enter B enter A leave A leave B enter C enter A leave A leave C leave D
可以发现A的初始化函数被执行了两次。 因为我们需要同时实现B和C的初始化函数,所以我们分别调用了两次,这是必然的结果。
但如果改写为 super? 呢?
class A(object): def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" super(B,self).__init__() print "leave B" class C(A): def __init__(self): print "enter C" super(C,self).__init__() print "leave C" class D(B,C): def __init__(self): print "enter D" super(D,self).__init__() print "leave D" >>> d=D() enter D enter B enter C enter A leave A leave C leave B leave D
会发现所有父类ABC都只执行了一次,A的初始化并没有像之前那样执行两次。
然后,我发现了一个很奇怪的事情:父类的执行是按照BCA的顺序,都是先输入再统一出来的。 这是MRO表的问题,稍后会讨论。
如果没有多重继承的话,super其实就类似于通过父类调用方法。 不过super还有一个好处:B继承A的时候写成A,如果你想根据自己的需要重构全部继承E,那么你就得全部改一次!
,可以发现super并不是那么简单。
维修工作台
什么是MRO'可以通过以下方式调用:
>>> D.mro() # or d.__class__.mro() or D.__class__.mro(D) [D, B, C, A, object] >>> B.mro() [B, A, object] >>> help(D.mro) #Docstring: #mro() -> list #return a type's method resolution order #Type: method_descriptor
MRO是类的方法分析序列表,其实就是继承父类的方法时的序列表(也可以理解为类继承序列表)。
这个表有什么用呢? 首先了解一下 super 的实际作用:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
换句话说,super方法实际上调用了MRO表中调用cls的下一个类。 如果是简单的一行单继承的话,就是父类->父类一一对应。 但对于多重继承,就需要遵循MRO表中的顺序。 以上面D的调用为例:
d 的初始化
-> D(进入D)超级(D,自我)
-> 父类B(输入B)super(B,self)
-> 父类C(输入C)super(C,self)
-> 父类A (进入A) (退出A) # 如果有则继续 super(A, self) -> (停止)
->(C出口)
-> (B出口)
->(出口D)
因此,MRO表中的超类初始化函数只执行一次!
那么,MRO的顺序是如何确定的呢? 关于这一点,请参考官方说明2.3命令。 基本上就是计算每个类的MRO(从父类到子类的顺序),然后合并成一行。 请遵循以下规则:
在MRO中,基类总是出现在派生类的后面。 如果有多个基类,则基类的相对顺序保持不变。 这个原则包括两点:
1.基类总是在派生类后面
2、类定义的继承顺序影响相对顺序。
假如有如下继承(super的详细用法在):
/\
/A
| /\
B-1 C-2 D-2
\//
E-1 /
\ /
那么MRO就是:F->E->B->C->D->A->
怎么解释呢?
按照官方的方法是:
L(O) = O L(B) = B O L(A) = A O L(C) = C A O L(D) = D A O L(E) = E + merge(L(B),L(C)) = E + merge(BO,CAO) = E + B + merge(O,CAO) = E + B + C + merge(O,AO) = E + B + C + A + merge(O,O) = E B C A O L(F) = F + merge(L(E),L(D)) = F + merge(EBCAO,DAO) = F + EBC + merge(AO,DAO) = F + EBC + D + merge(AO,AO) = F EBC D AO
看起来很复杂..但仍然可以得出,在MRO中,基类总是出现在派生类的后面。 如果有多个基类,则基类的相对顺序保持不变。 所以,我个人认为这样思考是可以的: