Python 深入理解super的用法
转载请标明出处(http://blog.csdn.net/lis_12/article/details/52870728).
带着问题去学super
Python中既然可以直接通过父类名调用父类的方法为什么还会存在super函数?
比如
class Child(Parent):
def __init__(self): Parent.__init__(self)
这种方式与super(Child, self).__init__()有区别呢?
准备知识:
MRO 全称 Method Resolution Order(方法解析顺序),它代表了类继承的顺序.
注:如果不懂MRO,参考这篇文章(http://blog.csdn.net/lis_12/article/details/52859376)
super()
谨记:super 指的是 MRO 中的下一个类! 不代表父类!!!!!!!
例如:MRO = [A,B,C,D]
super(A,self).__init__()代表A的下一个类B的__init__().
super()的代码实现:
def super(cls, inst):
mro = inst.__class__.mro() #获取inst的MRO
return mro[mro.index(cls) + 1] #获取cls在MRO中的索引,然后加1
参数说明:
- inst 负责生成 MRO 的 list;
- 通过 cls 定位当前 MRO 中的 index, 并返回 MRO[index + 1];
这两件事才是 super 的实质,一定要记住!
两种调用父类方法的区别
通过父类名直接调用父类方法
class D(object):
def __init__(self):
print 'enter D'
object.__init__(self)
print 'leave D'
class C(D):
def __init__(self):
print 'enter C'
D.__init__(self)
print 'leave C'
class B(D):
def __init__(self):
print 'enter B'
D.__init__(self)
print 'leave B'
class A(B,C):
def __init__(self):
print 'enter A'
B.__init__(self)
C.__init__(self)
print 'leave A'
if __name__ == '__main__':#主程序
a = A()
print A.__MRO__
'''
result:
enter A
enter B
enter D
leave D
leave B
enter C
enter D 初始化了两次--!
leave D
leave C
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
super()
#!/usr/bin/python
# -*- coding: utf-8 -*-
class D(object):
def __init__(self):
print 'enter D'
super(D,self).__init__()
self.x = 'D'
print 'leave D'
def fun(self):
print 'D fun()',self.x
class C(D):
def __init__(self):
print 'enter C'
super(C,self).__init__()
self.x = 'C'
print 'leave C'
def fun(self):
print 'C fun()',self.x
class B(D):
def __init__(self):
print 'enter B'
super(B,self).__init__()
print 'leave B'
class A(B,C):
def __init__(self):
print 'enter A'
super(A,self).__init__()
self.x = 'A'
print 'leave A'
if __name__ == '__main__':#主程序
a = A()
print A.__MRO__
a.fun() #按照MRO查找fun()方法,找到后停止查找,self.x = 'A'最后执行的所以self.x = A,如果把self.x = 'A'放到super(A,self).__init__(),self.x = 'C'就是最后执行的喽.
'''
result:
enter A
enter B
enter C
enter D
leave D D只初始化一次
leave C
leave B
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
C fun() A
'''
代码说明
通过父类名调用父类中的方法没啥可说的,顺序执行就好,然后就重复调用了父类D的__init__()方法- -!…重点说super.
使用super(A,self).__init__()执行流程:
- 首先进入类A的__init__()函数,打印’enter A’,当执行到super处,super(A,self)获取MRO中A的下一个类B,并返回,调用B.__init__();
- 进入B.__init__(),打印 ‘enter B’,当执行到super处,super(B,self)获取MRO中B的下一个类C,并返回,调用C.__init__();
- 进入C.__init__(),打印 ‘enter C’,当执行到super处,super(C,self)获取MRO中C的下一个类D,并返回,调用D.__init__();
- 进入D.__init__(),打印 ‘enter D’,当执行到super处,super(D,self)获取MRO中D的下一个类object,并返回,调用object.__init__();
- object.__init__()执行完成后,开始执行D.__init__()->C.__init__()->B.__init__()->A.__init__()中未执行的代码,依次打印leave D C B A;
总结
使用super()优点:
- 保证不会重复调用父类的__init__()函数;
- 代码更好维护,如果把父类的名字改了以后,使用类名调用方法还需要改类名,代码可维护性太差,使用super则能很好的解决这些问题;
使用super()注意事项:
super要么全用,要么全不用,哪怕父类是object也要用super,千万不要混用.
混用带来的坑:
class D(object):
def __init__(self):
print 'enter D'
super(D,self).__init__()
class C(D):
def __init__(self):
print 'enter C'
super(C,self).__init__()
class B(D):
def __init__(self):
print 'enter B'
D.__init__(self)
class A(B,C):
def __init__(self):
print 'enter A'
super(A,self).__init__()
if __name__ == '__main__':#主程序
a = A()
print A.__MRO__
'''
result:
enter A
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
从以上代码可知,没有调用C的__init__()方法.
__init__()调用顺序:A->B->D->object,绕过了C.
疑问
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Root(object):
def __init__(self):
print("this is Root")
class B(Root):
def __init__(self):
print("enter B")
# print(self) # this will print <__main__.D object at 0x...>
super(B, self).__init__()
print("leave B")
class C(Root):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
class D(B, C):
pass
d = D()
print(d.__class__.__MRO__)
'''
enter A D中没有重定义__init__()函数是怎么调用的呢?
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
个人理解:D中默认的__init__()使用了super()……如果有大神知道原因的话求指导,谢谢…