编程小白看到 super 函数就发怵,奇怪的语法难以清晰的描述。本文首先定义 super 的使用场景,再通过变换一个实例为 super 函数祛魅。
提到 super 自然绕不过类的继承关系。
子在构建时,通过继承,可以拥有父类所有的方法和属性。
super 是继承关系的拓展,第一个判定标准是:
如果不需要对父类方法做修改,子类无需显式的重写类方法
第二个标准是:
如果完全重写父类方法,子类也用不到 super 函数
所以,super 的使用场景在于:
需要重写父类方法,但又要完全保留父类方法中的代码
因此,super 的功能可以简化为 父类某方法代码段的插入和折叠
该实例截选自菜鸟教程
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
def bar(self, message):
print("%s from Parent" % message)
class FooChild(FooParent):
def __init__(self):
# super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类 FooChild 的对象转换为类 FooParent 的对象
super(FooChild, self).__init__()
print('Child')
def bar(self, message):
super(FooChild, self).bar(message)
print('Child bar fuction')
print(self.parent)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld')
# output:
# Parent
# Child
# HelloWorld from Parent
# Child bar fuction
# I'm the parent.
上述代码为正常的 super 函数使用案例。我们现在关注 bar
函数
把子类的 bar
函数完全注销掉,输出为:
Parent
Child
HelloWorld from Parent
这种情况完全继承自父类的对应方法。
把 super 那句注销掉即是完全重写。
def bar(self, message):
# super(FooChild, self).bar(message)
print('Child bar fuction')
print(self.parent)
输出为:
Parent
Child
Child bar fuction
I'm the parent.
子类方法完全覆盖父类方法,也没有用到 super
原实例即是一种修改方式,但为了更直观的展现
“super 的功能可以简化为 父类某方法代码段的插入和折叠
”
我们做一个 bar2
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
def bar(self, message):
print("%s from Parent" % message)
def bar2(self, message):
print("%s from Parent22222222222222222222222222222" % message)
print("%s from Parent22222222222222222222222222222" % message)
print("%s from Parent22222222222222222222222222222" % message)
print("%s from Parent22222222222222222222222222222" % message)
print("%s from Parent22222222222222222222222222222" % message)
class FooChild(FooParent):
def __init__(self):
# super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类 FooChild 的对象转换为类 FooParent 的对象
super(FooChild, self).__init__()
print('Child')
def bar(self, message):
print('Child bar fuction')
print(self.parent)
super(FooChild, self).bar2(message)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld')
bar2
是一大段,冗长的代码。子类需要这段代码。在需要的位置,使用 super 函数插入即可。有意思的是:
插入
和 折叠
的特点。上述代码的输出为:
Parent
Child
Child bar fuction
I'm the parent.
HelloWorld from Parent22222222222222222222222222222
HelloWorld from Parent22222222222222222222222222222
HelloWorld from Parent22222222222222222222222222222
HelloWorld from Parent22222222222222222222222222222
HelloWorld from Parent22222222222222222222222222222
可以看到,我们并没有在子类方法中实现冗长的 bar2
函数,使用 super 函数很好的降低了工作量。
从 python 3.x 开始,super 可以使用更简练的形式。如下:
class FooChild(FooParent):
def __init__(self):
super().__init__()
print('Child')
def bar(self, message):
print('Child bar fuction')
print(self.parent)
super().bar2(message)
对比:
super(FooChild, self).bar2(message)
&
super().bar2(message)
前面提到,super 可以看作父类方法的 插入
和 折叠
,因此 子类的传参必须要满足所有父类的参数,此外需要注意子类独有的参数。
如下:
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
def bar(self, message):
print("%s from Parent" % message)
def bar2(self, message1, message2, message3, message4, message5):
print("%s from Parent22222222222222222222222222222" % message1)
print("%s from Parent22222222222222222222222222222" % message2)
print("%s from Parent22222222222222222222222222222" % message3)
print("%s from Parent22222222222222222222222222222" % message4)
print("%s from Parent22222222222222222222222222222" % message5)
class FooChild(FooParent):
def __init__(self):
super().__init__()
print('Child')
def bar(self, message1, message2, message3, message4, message5, new_message):
print('Child bar fuction')
print(self.parent)
print(new_message)
super().bar2(message1, message2, message3, message4, message5)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld1', 'HelloWorld2', 'HelloWorld3', 'HelloWorld4', 'HelloWorld5', 'HelloWorld')
但是这样的设计违背了 里氏替换原则,即,父类对 bar
函数的测试案例因为缺少参数而不能通过子类的测试,虽然运行起来不会出现问题,但显然不符合代码规范(pycharm会提示警告)
一种合理的方法可以是:
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
def bar(self, message):
print("%s from Parent" % message)
def bar2(self, message1, message2, message3, message4, message5):
print("%s from Parent22222222222222222222222222222" % message1)
print("%s from Parent22222222222222222222222222222" % message2)
print("%s from Parent22222222222222222222222222222" % message3)
print("%s from Parent22222222222222222222222222222" % message4)
print("%s from Parent22222222222222222222222222222" % message5)
class FooChild(FooParent):
def __init__(self):
super().__init__()
print('Child')
def bar(self, new_message):
print('Child bar fuction')
print(self.parent)
print(new_message)
super().bar2(new_message, new_message, new_message, new_message, new_message)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld1')