Python基础教程:super()方法详解

水深无声 2022-10-12 15:55 245阅读 0赞

一、单独调用父类的方法

需求:编写一个类,然后再写一个子类进行继承,使用子类去调用父类的方法1。

使用方法1打印: 胖子老板,来包槟榔。

那么先写一个胖子老板的父类,执行一下:

  1. class FatFather(object):
  2. def __init__(self, name):
  3. print('FatFather的init开始被调用')
  4. self.name = name
  5. print('FatFather的name是%s' % self.name)
  6. print('FatFather的init调用结束')
  7. def main():
  8. ff = FatFather("胖子老板的父亲")

运行一下这个胖子老板父类的构造方法__init__ 如下:

  1. if __name__ == "__main__":
  2. main()
  3. FatFatherinit开始被调用
  4. FatFathername是胖子老板的父亲
  5. FatFatherinit调用结束

好了,那么下面来写一个子类,也就是胖子老板类,继承上面的类

胖子老板的父类

  1. ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! '''
  2. class FatFather(object):
  3. def __init__(self, name):
  4. print('FatFather的init开始被调用')
  5. self.name = name
  6. print('调用FatFather类的name是%s' % self.name)
  7. print('FatFather的init调用结束')
  8. #胖子老板类 继承 FatFather 类
  9. class FatBoss(FatFather):
  10. def __init__(self, name, hobby):
  11. print('胖子老板的类被调用啦!')
  12. self.hobby = hobby
  13. FatFather.__init__(self, name) #直接调用父类的构造方法
  14. print("%s 的爱好是 %s" % (name, self.hobby))
  15. def main():
  16. ff = FatFather("胖子老板的父亲")
  17. fatboss = FatBoss("胖子老板", "打斗地主")

在这上面的代码中,我使用FatFather.init(self,name)直接调用父类的方法。
运行结果如下:

  1. if __name__ == "__main__":
  2. main()
  3. 胖子老板的类被调用啦!
  4. FatFatherinit开始被调用
  5. 调用FatFather类的name是胖子老板
  6. FatFatherinit调用结束
  7. 胖子老板 的爱好是 打斗地主

二、super() 方法基本概念

除了直接使用FatFather.__init__(self,name) 的方法,还可以使用super()方法来调用。

那么首先需要看super()方法的描述和语法理解一下super() 方法的使用。

1 描述

super() 函数是用于调用父类(超类)的一个方法。

super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

2 语法

以下是 super() 方法的语法:

  1. super(type[, object-or-type])

参数

  • type – 类
  • object-or-type – 类,一般是 self

Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

实例:

  1. class A:
  2. pass
  3. class B(A):
  4. def add(self, x):
  5. super().add(x)

3 单继承使用super()

使用super() 方法来改写刚才胖子老板继承父类的 init 构造方法

胖子老板的父类

  1. class FatFather(object):
  2. def __init__(self, name):
  3. print('FatFather的init开始被调用')
  4. self.name = name
  5. print('调用FatFather类的name是%s' % self.name)
  6. print('FatFather的init调用结束')
  7. #胖子老板类 继承 FatFather 类
  8. class FatBoss(FatFather):
  9. def __init__(self, name, hobby):
  10. print('胖子老板的类被调用啦!')
  11. self.hobby = hobby
  12. FatFather.__init__(self,name) #直接调用父类的构造方法
  13. super().__init__(name)
  14. print("%s 的爱好是 %s" % (name, self.hobby))
  15. def main():
  16. ff = FatFather("胖子老板的父亲")
  17. fatboss = FatBoss("胖子老板", "打斗地主")
  18. #从上面使用super方法的时候,因为是单继承,直接就可以使用了。
  19. #运行如下:
  20. if __name__ == "__main__":
  21. main()
  22. 胖子老板的类被调用啦!
  23. FatFatherinit开始被调用
  24. 调用FatFather类的name是胖子老板
  25. FatFatherinit调用结束
  26. 胖子老板 的爱好是 打斗地主那么为什么说单继承直接使用就可以呢?因为super()方法如果多继承的话,会涉及到一个MRO(继承父类方法时的顺序表) 的调用排序问题。

下面可以打印一下看看单继承的MRO顺序(FatBoss.mro)。

胖子老板的父类

  1. ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! '''
  2. class FatFather(object):
  3. def __init__(self, name):
  4. print('FatFather的init开始被调用')
  5. self.name = name
  6. print('调用FatFather类的name是%s' % self.name)
  7. print('FatFather的init调用结束')
  8. 胖子老板类 继承 FatFather
  9. class FatBoss(FatFather):
  10. def __init__(self, name, hobby):
  11. print('胖子老板的类被调用啦!')
  12. self.hobby = hobby
  13. FatFather.__init__(self,name) 直接调用父类的构造方法
  14. super().__init__(name)
  15. print("%s 的爱好是 %s" % (name, self.hobby))
  16. def main():
  17. print("打印FatBoss类的MRO")
  18. print(FatBoss.__mro__)
  19. print()
  20. print("=========== 下面按照 MRO 顺序执行super方法 =============")
  21. fatboss = FatBoss("胖子老板", "打斗地主")

上面的代码使用 FatBoss.mro 可以打印出 FatBoss这个类经过 python解析器的 C3算法计算过后的继承调用顺序。

运行如下:

  1. if __name__ == "__main__":
  2. main()
  3. #打印FatBoss类的MRO
  4. (<class '__main__.FatBoss'>, <class '__main__.FatFather'>, <class 'object'>)
  5. =========== 下面按照 MRO 顺序执行super方法 =============
  6. 胖子老板的类被调用啦!
  7. FatFatherinit开始被调用
  8. 调用FatFather类的name是胖子老板
  9. FatFatherinit调用结束
  10. 胖子老板 的爱好是 打斗地主

从上面的结果 <class 'main.FatBoss'>, <class 'main.FatFather'>, <class 'object'>可以看出,super() 方法在 FatBoss 会直接调用父类是 FatFather ,所以单继承是没问题的。

那么如果多继承的话,会有什么问题呢?

4 多继承使用super()

假设再写一个胖子老板的女儿类,和 胖子老板的老婆类,此时女儿需要同时继承 两个类(胖子老板类,胖子老板老婆类)。

因为胖子老板有一个爱好,胖子老板的老婆需要干活干家务,那么女儿需要帮忙同时兼顾。

此时女儿就是需要继承使用这两个父类的方法了,那么该如何去写呢?

下面来看看实现代码:

  1. # 胖子老板的父类
  2. class FatFather(object):
  3. def __init__(self, name, *args, **kwargs):
  4. print()
  5. print("=============== 开始调用 FatFather ========================")
  6. print('FatFather的init开始被调用')
  7. self.name = name
  8. print('调用FatFather类的name是%s' % self.name)
  9. print('FatFather的init调用结束')
  10. print()
  11. print("=============== 结束调用 FatFather ========================")
  12. # 胖子老板类 继承 FatFather 类
  13. class FatBoss(FatFather):
  14. def __init__(self, name, hobby, *args, **kwargs):
  15. print()
  16. print("=============== 开始调用 FatBoss ========================")
  17. print('胖子老板的类被调用啦!')
  18. super().__init__(name)
  19. # 因为多继承传递的参数不一致,所以使用不定参数
  20. super().__init__(name, *args, **kwargs)
  21. print("%s 的爱好是 %s" % (name, hobby))
  22. print()
  23. print("=============== 结束调用 FatBoss ========================")
  24. # 胖子老板的老婆类 继承 FatFather类
  25. class FatBossWife(FatFather):
  26. def __init__(self, name, housework, *args, **kwargs):
  27. print()
  28. print("=============== 开始调用 FatBossWife ========================")
  29. print('胖子老板的老婆类被调用啦!要学会干家务')
  30. super().__init__(name)
  31. # 因为多继承传递的参数不一致,所以使用不定参数
  32. super().__init__(name, *args, **kwargs)
  33. print("%s 需要干的家务是 %s" % (name, housework))
  34. print()
  35. print("=============== 结束调用 FatBossWife ========================")
  36. # 胖子老板的女儿类 继承 FatBoss FatBossWife类
  37. class FatBossGril(FatBoss, FatBossWife):
  38. def __init__(self, name, hobby, housework):
  39. print('胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主')
  40. super().__init__(name, hobby, housework)
  41. def main():
  42. print("打印FatBossGril类的MRO")
  43. print(FatBossGril.__mro__)
  44. print()
  45. print("=========== 下面按照 MRO 顺序执行super方法 =============")
  46. gril = FatBossGril("胖子老板", "打斗地主", "拖地")

运行结果如下:

  1. if __name__ == "__main__":
  2. main()
  3. #打印FatBossGril类的MRO
  4. (<class '__main__.FatBossGril'>, <class '__main__.FatBoss'>, <class '__main__.FatBossWife'>, <class '__main__.FatFather'>, <class 'object'>)
  5. =========== 下面按照 MRO 顺序执行super方法 =============
  6. 胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主
  7. =============== 开始调用 FatBoss ========================
  8. 胖子老板的类被调用啦!
  9. =============== 开始调用 FatBossWife ========================
  10. 胖子老板的老婆类被调用啦!要学会干家务
  11. =============== 开始调用 FatFather ========================
  12. FatFatherinit开始被调用
  13. 调用FatFather类的name是胖子老板
  14. FatFatherinit调用结束
  15. =============== 结束调用 FatFather ========================
  16. 胖子老板 需要干的家务是 拖地
  17. =============== 结束调用 FatBossWife ========================
  18. 胖子老板 的爱好是 打斗地主
  19. =============== 结束调用 FatBoss ========================

从上面的运行结果来看,我特意给每个类的调用开始以及结束都进行打印标识,可以看到。

每个类开始调用是根据MRO顺序进行开始,然后逐个进行结束的。

还有就是由于因为需要继承不同的父类,参数不一定。

所以,所有的父类都应该加上不定参数*args , **kwargs ,不然参数不对应是会报错的。

三、注意事项

  • super().__init__相对于类名.__init__,在单继承上用法基本无差
  • 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,可以尝试写个代码来看输出结果
  • 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
  • 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
  • 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

四、练习

以下的代码的输出将是什么? 说出你的答案并解释。

  1. class Parent(object):
  2. x = 1
  3. class Child1(Parent):
  4. pass
  5. class Child2(Parent):
  6. pass
  7. print(Parent.x, Child1.x, Child2.x)
  8. 1 1 1
  9. Child1.x = 2
  10. print(Parent.x, Child1.x, Child2.x)
  11. 1 2 1
  12. #注意:Child1已经拥有了属于自己的x
  13. Parent.x = 3
  14. print(Parent.x, Child1.x, Child2.x)
  15. 3 2 3

发表评论

表情:
评论列表 (有 0 条评论,245人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Python super详解

    说到 super, 大家可能觉得很简单呀,不就是用来调用父类方法的嘛。如果真的这么简单的话也就不会有这篇文章了,且听我细细道来。 约定 在开始之前我们来约定一下本文所使

    相关 JAVA基础Super()详解

    什么是super? 是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。 如果没有super存在的问题: 继承中子类可能会重写父类的方法,子类