super(E , F()).fun() # 输出结果:B.fun super(D , F()).fun() # 输出结果:A.fun super(F , F()).fun() # 输出结果:D.fun
再回顾一下__mro__的顺序:F->D->A->E->B->C->object,发现规律没?调用的都是type对应的类在__mro__顺序中的下一个类的fun方法。所以,我们可以通过type参数来指定调用父类的范围。
再让type保持不变,obj尝试不同的实例:
super(B , F()).fun() # 输出结果:C.fun super(B , E()).fun() # 输出结果:C.fun super(B , B()).fun() # 这是错误的,会报错
发现规律了吗?上面这个类继承关系太简单,可能规律并不明显。事实上,obj参数指定的是用那个类的__mro__属性。
好了,我们现在回到图2中使用super()之后的代码,来解释一下为什么输出顺序是A->C->B->D。首先我们要明白,D类的__mro__顺序是D->B->C->A,在D类中调用fun方法,然后在D类fun方法中遇到super(D , self).fun(),这个self指的是D类的实例化对象,所以用的是D类的__mro__顺序,而且指明位置是D后面也就是B类,所以继续调用B类的fun方法,遇到super(B , self).fun(),这时候需要注意,这里的self还是原来的D类实例(千万注意不是B类实例),所以还是用D类的__mro__顺序,那就继续调用下一个C类的fun方法,同理继续调用下一个父类,也就是A类的fun方法,执行完A类的fun方法后,回到C的fun方法中,打印输出,然后回到B类的fun方法,知道D类的fun方法打印输出完。懂了吗?
4.2 super() super()事实上是懒人版的super(type , obj),这种方式只能用在类体内部,Python会自动把两个参数填充上,type指代当前类,obj指导当前类的实例对象,相当于super(__class__ , self)。所以,以下三种代码是完全等效的:
代码一:
class B(A): def fun(self): super().fun() print('B.fun')
代码二:
class B(A): def fun(self): super(B , self).fun() print('B.fun')
代码三:
class B(A): def fun(self): super(__class__ , self).fun() print('B.fun')
4.3 super(type_1 , type_2)
当super传入的两个参数都是类名是,type_2必须是type_1的子类。功能上与super(type , obj)有什么不同呢?我们继续上一小节的代码输出测试:
print(super(F , F())) #输出结果为:<super: <class 'F'>, <F object>> print(super(F , F)) #输出结果为:<super: <class 'F'>, <F object>>
输出结果是一样的,那你就以为super(type_1 , type_2)与super(type , obj)一样吗?看下面输出:
print(super(F , F()).fun()) #输出结果为:D.fun print(super(F , F).fun()) # 报错:TypeError: fun() missing 1 required positional argument: 'self'
所以,super(type_1 , type_2)与super(type , obj)有区别,在看一下下列输出:
print(super(F , F()).fun)# 输出结果:<bound method D.fun of <__main__.F object at 0x000001BD44A98B38>> print(super(F , F).fun) # 输出结果:<function D.fun at 0x000001BD44A9EE18> print(D.fun) # 输出结果:<function D.fun at 0x000001BD44A9EE18>
所以,当super传入的两个传输都是类时,得到的就是一个指向继承顺序下的类的代理,并未绑定实例,要调用D类的fun方法,还需传入实例:
print(super(F , F).fun(F())) #输出结果:D.fun