Python-super()函数

  1. 子类中调用父类中被重写了的方法(super()函数)
    1. 直接在子类中调用父类
    2. 使用super(class_obj, ins_obj)函数调用父类
    3. 使用super()函数调用父类

子类中调用父类中被重写了的方法(super()函数)

子类重写父类的构造函数,那么父类定义的构造函数就不能用了,假如父类中的构造函数定义了很多属性,而子类想要继承这些属性,由于重写了__init__()方法,不能使用父类中定义的构造函数,就需要自己再一个个的重写那些属性,就造成了很多重复代码,为了避免这种情况,python可以在子类中调用父类的方法,有以下三种方式:

直接在子类中调用父类

例如

#!/usr/bin/python3

class ParentClass(object):
    def __init__(self, name):
        self.name = name


class ChildClass(ParentClass):
    def __init__(self, name, age):
        ParentClass.__init__(self, name)           # 直接调用父类需要传入self,相当于直接调用函数,需要传入参数,父类有self,name两个形参,子类调用时需要传入两个实参
        self.age = age

c1 = ChildClass('tom', 23)
print(c1.name, c1.age)
[root@lyucan ~]# ./18.py
tom 23

使用super(class_obj, ins_obj)函数调用父类

class_obj表示类对象,该参数表示:根据继承的MRO,从class_obj右边第一个类开始依次搜索方法。ins_obj表示实例对象, 一般为self。
调用super()函数时,方法的参数不需要写self,因为self在super()函数中传递。
例如:

#!/usr/bin/python3

class ParentClass(object):
    def __init__(self, name):
        self.name = name


class ChildClass(ParentClass):
    def __init__(self, name, age):
        super(ChildClass, self).__init__(name)        
        # 这行代码表示:根据self实例对象(即下面的c1)对应的类对象(即ChildClass)的MRO,从ChildClass(第一个参数)类对象的右边一个对象(即ParentClass)开始搜索__init__方法,将搜索到的方法绑定给self实例对象,这里的self指的是ChildClass类对象的实例对象,即下面的c1
        self.age = age

c1 = ChildClass('tom', 23)
print(c1.name, c1.age)

print(ChildClass.__mro__)
[root@lyucan ~]# ./18.py
tom 23
(<class '__main__.ChildClass'>, <class '__main__.ParentClass'>, <class 'object'>)

假如子类有两个父类,两个父类都有重写的方法,但是第一个父类不是我们想要的,第二个父类才是我们想要的,这个时候,python只要搜索到方法名相同的就进行调用,不会继续往后搜索,例如:

#!/usr/bin/python3

class ParentClass(object):
    def __init__(self, name):
        self.name = name

class ParentClass1(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class ChildClass(ParentClass, ParentClass1):
    def __init__(self, name, age, gender):
        super().__init__(name, age)               # 想要调用的是父类ParentClass1中的__init__方法
        self.gender = gender

c1 = ChildClass('tom', 23, 'male')
print(c1.name, c1.age, c1.gender)

print(ChildClass.__mro__)
[root@lyucan ~]# ./19.py
Traceback (most recent call last):
  File "./19.py", line 17, in <module>
    c1 = ChildClass('tom', 23, 'male')
  File "./19.py", line 14, in __init__
    super().__init__(name, age)
TypeError: __init__() takes 2 positional arguments but 3 were given   # 但是这里找到的是ParentClass中的__init__方法,并直接进行调用

当我们注释掉ParentClass中的__init__后,就会找到PraentClass1中的__init__方法:

#!/usr/bin/python3

class ParentClass(object):
#    def __init__(self, name):
#        self.name = name
    pass
class ParentClass1(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class ChildClass(ParentClass, ParentClass1):
    def __init__(self, name, age, gender):
        super().__init__(name, age)
        self.gender = gender

c1 = ChildClass('tom', 23, 'male')
print(c1.name, c1.age, c1.gender)

print(ChildClass.__mro__)
[root@lyucan ~]# ./19.py
tom 23 male
(<class '__main__.ChildClass'>, <class '__main__.ParentClass'>, <class '__main__.ParentClass1'>, <class 'object'>)

如果需要跳过ParentClass,调用父类的__init__方法,可以做如下修改:

#!/usr/bin/python3

class ParentClass(object):
    def __init__(self, name):
        self.name = name

class ParentClass1(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class ChildClass(ParentClass, ParentClass1):
    def __init__(self, name, age, gender):
        super(ParentClass, self).__init__(name, age)  # 定义了从ParentClass的右边第一个开始搜索,不包括ParentClass,因此,调用的就是ParentClass1中的__init__方法
        self.gender = gender

c1 = ChildClass('tom', 23, 'male')
print(c1.name, c1.age, c1.gender)

print(ChildClass.__mro__)
[root@lyucan ~]# ./19.py
tom 23 male
(<class '__main__.ChildClass'>, <class '__main__.ParentClass'>, <class '__main__.ParentClass1'>, <class 'object'>)

使用super()函数调用父类

不传参数,默认就是super()函数所在的类对象和self(即该类对象的实例对象),例如

#!/usr/bin/python3

class ParentClass(object):
    def __init__(self, name):
        self.name = name


class ChildClass(ParentClass):
    def __init__(self, name, age):
        super().__init__(name)               # 调用super()函数时,方法的参数不需要传递self
        self.age = age

c1 = ChildClass('tom', 23)
print(c1.name, c1.age)
[root@lyucan ~]# ./18.py
tom 23

直接调用和使用super()的优劣:

  • 直接调用简单直观,但是对于钻石继承,直接调用会调用两边父类的方法
  • 使用super()方法可以有效的避免钻石继承带来的问题,但是理解起来有点不那么直观

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com