《Python面试每日一题》之super

问题描述:

在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算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章