玩转 Python super 函数

编程小白看到 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 函数插入即可。有意思的是:

  1. super 函数可以不在第一行(虽然编程规范都是在第一行),它可以在需要出现的任何地方。这体现了 插入折叠 的特点。
  2. 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 函数很好的降低了工作量。

简化的 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')

你可能感兴趣的:(python)