问题描述:
在Python中,当子类引用父类的函数时,有几种方式?阐明不同方式的区别和MRO机制。
答:
子类引用父类的函数时有三种方法。
1. 父类名.函数名(self, 参数)
2. super().函数名(参数)
3. super(类名).函数名(参数)
Python支持多继承机制,即一个类可以有多个父类。当使用第一种方式调用父类的函数时,父类名写谁,代码就去调用哪个父类的函数,当两个父类同时继承于一个父类时,如图所示,这种调用方式就会产生内存的冗余。
# 定义一个父类
class father(object):
def __init__(self,name,*args,**kwargs):
self.name = name
print("Father类调用1次" )
# 定义第一个子类1,继承于父类
class son_1(father):
def __init__(self,name,gender,*args,**kwargs):
father.__init__(self,name,*args,**kwargs)
self.gender = gender
# 定义第二个子类2,野继承于父类
class son_2(father):
def __init__(self,name,age,*args,**kwargs):
father.__init__(self,name,*args,**kwargs)
self.age = age
# 定义一个“孙子类”,同时继承于子类1和子类2
class grandson(son_1,son_2):
def __init__(self,name,gender,age,language,*args,**kwargs):
son_1.__init__(self,name,gender,*args,**kwargs)
son_2.__init__(self,name,age,*args,**kwargs)
self.language = language
# 创建一个“孙子类”对象
tomato = grandson("tomato","male","21","python")
当创建一个grandson对象时,会自动调用grandson类的init函数,初始化对象。
因为在grandson类的init函数中使用了 父类名.函数名(self,参数) 的方式调用son_1和son_2的init函数,且son_1和son_2同时也用了此方式调用father类的init函数,
所以father类的init函数会被机械的执行两次,造成内存冗余。
运行上述代码,结果如下。
Father类调用1次
Father类调用1次
这种多继承父类同时继承于一个父类的情况,使用 super().函数名(参数) 的方式调用父类函数,就不会造成内存冗余了。
这是因为MRO机制在发挥着作用,在Python中存在一种算法叫C3算法,其结果是生成一个元组,这个元组会存在类的__mro__内置属性里面,元组中元素的顺序就是调用super函数时的调用顺序。
我们将上述代码修改一下,将所有的 父类名.函数名(self,参数) 改为 super().函数名(参数) 。
class father(object):
def __init__(self,name,*args,**kwargs):
self.name = name
print("Father类调用1次" )
class son_1(father):
def __init__(self,name,gender,*args,**kwargs):
self.gender = gender
super().__init__(name,*args,**kwargs)
class son_2(father):
def __init__(self,name,age,*args,**kwargs):
self.age = age
super().__init__(name,*args,**kwargs)
class grandson(son_1,son_2):
def __init__(self,name,gender,age,language,*args,**kwargs):
self.language = language
super().__init__(name,gender,age,*args,**kwargs)
tomato = grandson("tomato","male","21","python")
print(grandson.__mro__)
运行上述代码,结果如下:
Father类调用1次
(<class '__main__.grandson'>, <class '__main__.son_1'>, <class '__main__.son_2'>, <class '__main__.father'>, <class 'object'>)
这时,Father类的init函数只被调用了一次。
需要注意的是,在son_1类的init函数中使用super().函数名(参数) 时,此时线程不是执行的Father类的init函数,而是执行的son_2的init函数,因为在__mro__内置属性的元组中,son_2在son_1的后面。
简而言之,MRO机制就是一种基于C3算法的父类调用顺序。它保证了Python在多继承中,每个父类的函数只会执行一次,防止了重复调用。
若有需要,可点击前往了解何为C3算法:详解C3算法