调用父类方法
为了调用父类(超类)的一个方法,可以使用 super() 函数,比如:
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
class A(object):
def spam(self):
print('A.spam')
class B(A):
def spam(self):
print('B.spam')
#A.spam(self) #直接调用父类方法
super(B,self).spam()
b = B()
b.spam()
super() 函数的一个常见用法是在 __init__()
方法中确保父类被正确的初始化了:
class A(object):
def __init__(self):
self.x = 0
class B(A):
def __init__(self):
super(B,self).__init__()
self.y = 1
b = B()
print(b.x)
print(b.y)
不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!
super 其实干的是这件事:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
两个参数 cls 和 inst 分别做了两件事:
1. inst 负责生成 MRO 的 list
2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]
这两件事才是 super 的实质,一定要记住!
MRO 全称 Method Resolution Order,它代表了类继承的顺序。
class Root(object):
def __init__(self):
print("this is Root")
class A(Root):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Root):
def __init__(self):
print("enter B")
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(A, B, C):
def __init__(self):
print("enter D")
super(D, self).__init__()
print("leave D")
d = D()
print(d.__class__.__mro__)
#输出结果:
#enter D
#enter A
#enter B
#enter C
#this is Root
#leave C
#leave B
#leave A
#leave D
#(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)
原理: 对于你定义的每一个类而言,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。如:
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
老实说,你所要知道的就是MRO列表中的类顺序会让你定义的任意类层级关系变得有意义。
当你使用 super() 函数时,Python会在MRO列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次
note
super ,MRO ,都是针对 new-style class。如果不是 new-style class,就只能用父类的类名去调用函数啦。