[关闭]
@spiritnotes 2016-01-31T09:14:05.000000Z 字数 1756 阅读 1961

Python语言实践分析 -- super函数

Python

实践

在面向对象编程时,继承是必不可少的,而定义时往往需要在子类的一些操作时(如初始化等),除了处理子类自己的部分外,还需要调用基类的对应同名函数。在Python中有两种方法,一种是直接使用父类的类名,另一种是使用super函数,这两者间有什么区别呢?分以下两种情况分析:

单继承

在单继承情况下,两者完全没有差别。

  1. class Base():
  2. def __init__(self):
  3. print('Base')
  4. class A(Base):
  5. def __init__(self):
  6. print('A')
  7. super(A, self).__init__()
  8. class B(Base):
  9. def __init__(self):
  10. print('B')
  11. Base.__init__(self)
  12. a, b = A(), B()

菱形继承

但是在于多继承中,两者就有差别了,这里以最容易说明问题的菱形继承来举例,也就是C++中的虚继承场景。考虑如下示例代码,先看一下不使用super函数的场景:

  1. class Base():
  2. def __init__(self):
  3. print('Base')
  4. class A(Base):
  5. def __init__(self):
  6. print('A')
  7. Base.__init__(self)
  8. class B(Base):
  9. def __init__(self):
  10. print('B')
  11. Base.__init__(self)
  12. class C(A, B):
  13. def __init__(self):
  14. print('C')
  15. A.__init__(self)
  16. B.__init__(self)
  17. c= C()

其结果显示如下:

  1. C
  2. A
  3. Base
  4. B
  5. Base

可以看到其中的Base的初始化函数执行了2次,再换做super函数,代码如下:

  1. class Base():
  2. def __init__(self):
  3. print('Base')
  4. class A(Base):
  5. def __init__(self):
  6. print('A')
  7. super(A, self).__init__()
  8. class B(Base):
  9. def __init__(self):
  10. print('B')
  11. super(B, self).__init__()
  12. class C(A, B):
  13. def __init__(self):
  14. print('C')
  15. super(C, self).__init__()
  16. c= C()

其结果如下,可见Base的初始化函数只执行了一次。

  1. C
  2. A
  3. B
  4. Base

原理

Python中的属性查找与类继承密切相关,其会依次在当前对象属性以及其父类中递归查找,其各个父类的顺序就相当重要。Python中采用一种MRO计算方法来对继承结构中的类进行排序(新类),通过属性__mro__可以查看每个类的继承顺序。

  1. print(C.__mro__)
  2. (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

而super函数也即是针对每个类的__mro__中查找对应的类的下一个类,其执行效果相当于如下代码:

  1. class C(A, B):
  2. def __init__(self):
  3. print('C')
  4. #super(C, self).__init__()
  5. mro = self.__class__.__mro__
  6. mro[mro.index(C)+1].__init__(self)

因此在实际使用尽量使用super,特别注意不可两者方式混用。

MRO算法

MRO算法是指的python中查找类的属性寻找算法,其按照先后顺序找出类之间的继承关系,保证任何一个父类必定排在其子类的后面,算法如下:
1. 依据继承关系形成多条继承链;
2. 从左到右依次选择链首的类,从中找出第一个满足如下条件的类:其不出现在其他链中,或者处于其他链的链头,将其加入到__mro__中,并在所有链中删除
3. 然后重新执行过程2,直到类都已经加入到__mro__中;

针对示例中的类,其执行顺序如下:
1. 形成两条链 [C, A, Base], [C, B, Base]
2. 依次选择C, A, B, Base,最后形成__mro__

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注