子类中调用父类中被重写了的方法(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