AI智能
改变未来

Python – 面向对象编程 – super()


前置知识

  • 继承的详解:https://www.cnblogs.com/poloyy/p/15216652.html

  • 子类方法的重写:https://www.cnblogs.com/poloyy/p/15221352.html

  • 多继承的详解:https://www.cnblogs.com/poloyy/p/15224912.html

为什么要用 super()

当子类重写了父类方法时,又想调用父类的同名方法时,就需要用到 super()

什么是 super

  • 在 Python 中,super 是一个特殊的类
  • super() 就是使用 super 类创建出来的对象
  • 实际应用的场景:子类在重写父类方法时,调用父类方法

单继承中使用 super

实例方法使用 super

类图

实际代码

class A:def __init__(self):self.n = 1def add(self, m):print(f\'AAA [self] is {id(self)}\')print(f\'AAA [self.n] is {self.n}\')self.n += mclass B(A):def __init__(self):self.n = 100# 重写父类方法def add(self, m):# 子类特有代码print(f\'BBB [self] is {id(self)}\')print(f\'BBB [self.n] is {self.n}\')# 调用父类方法super().add(m)self.n += mb = B()b.add(2)print(b.n)# 输出结果BBB [self] is 4489158560BBB [self.n] is 100AAA [self] is 4489158560AAA [self.n] is 100104
  1. super().add()的确调用了父类方法
  2. 重点:此时父类方法的 self 并不是父类实例对象,而是子类实例对象

构造方法使用 super

class Animal:def __init__(self, name):self.name = namedef prints(self):print(\"Animale name is \", self.name)class Dog(Animal):def __init__(self, name, age):# 调用父类的 init 构造方法super(Dog, self).__init__(name)self.age = agedef prints(self):# 调用父类的方法super(Dog, self).prints()print(\"Dog age is \", self.age)dog = Dog(\"小汪\", 10)dog.prints()# 输出结果Animale name is  小汪Dog age is  10

这里用了super(子类名, self),和上面的 super() 是一样效果

调用父类方法有两种方式

  • super().父类方法():注意,只能在 Python 3.x 中使用
  • super(子类名, self).父类方法()

其实还有第三种

在 Python 2.x 的时候,如果需要调用父类的方法,还可以用

父类名.方法(self)
  • 这种方式,Python 3.x 还是支持的
  • 不过不推荐,因为父类名发生变化的话,方法调用位置的类名也要同步修改

通过父类名调用父类方法(不推荐)

class Animal:def __init__(self, name):self.name = namedef prints(self):print(\"Animale name is \", self.name)class Dog(Animal):def __init__(self, name, age):# 调用父类的 init 构造方法Animal.__init__(self, name)self.age = agedef prints(self):# 调用父类的方法Animal.prints(self)print(\"Dog age is \", self.age)dog = Dog(\"小汪\", 10)dog.prints()# 输出结果Animale name is  小汪Dog age is  10

通过父类名调用的这种方式,是需要传 self 参数的哦

温馨提示

在开发时,父类名.方法(),super().方法()两种方式不要混用哈

灵魂拷问一:既然已经重写了子类的构造方法,为什么还要去调用 super?

子类需要重写父类方法来实现子类独有的功能,但同时又需要依赖父类方法来完成某些逻辑

实际栗子

  • 在实现多线程的时候(后面会详细展开说多线程)
  • 父类 Thread 的构造方法包含了很多逻辑代码
  • 子线程虽然需要实现子类独有功能,但仍需父类方法来处理其他逻辑

from threading import Threadclass MyThread(Thread):def __init__(self, name):# 1、实现子类独有功能print(\"子类线程 %s\" % name)# 2、需要依赖父类方法完成其他功能super().__init__(name=name)

多继承中使用 super

类图

实际代码

# 多继承class Animal:def __init__(self, animalName):print(animalName, \'is an animal.\')# Mammal 继承 Animalclass Mammal(Animal):def __init__(self, mammalName):print(mammalName, \'is a mammal.\')super().__init__(mammalName)# CannotFly 继承 Mammalclass CannotFly(Mammal):def __init__(self, mammalThatCantFly):print(mammalThatCantFly, \"cannot fly.\")super().__init__(mammalThatCantFly)# CannotSwim 继承 Mammalclass CannotSwim(Mammal):def __init__(self, mammalThatCantSwim):print(mammalThatCantSwim, \"cannot swim.\")super().__init__(mammalThatCantSwim)# Cat 继承 CannotSwim 和 CannotFlyclass Cat(CannotSwim, CannotFly):def __init__(self):print(\'I am a cat.\');super().__init__(\'Cat\')# Driver codecat = Cat()print(\'\')bat = CannotSwim(\'Bat\')# 输出结果I am a cat.Cat cannot swim.Cat cannot fly.Cat is a mammal.Cat is an animal.Bat cannot swim.Bat is a mammal.Bat is an animal.

好像挺奇怪的,从输出结果看,为什么CannotSwim 类里面的 super().__init__() 调用的是CannotFly 类里面的方法呢?不是应该调用CannotSwim 的父类 Mamal 的方法吗?

灵魂拷问二:super 的执行顺序到底是什么?

  • 其实 super() 并不一定调用父类的方法
  • super() 是根据类的 MRO 方法搜索顺序来决定调用谁的
  • super() 真正调用的是 MRO 中的下一个类,而不一定是父类
  • 当然,这种情况只会出现在多继承

先来看看 Cat 的 MRO

print(Cat.__mro__)(<class \'__main__.Cat\'>, <class \'__main__.CannotSwim\'>, <class \'__main__.CannotFly\'>, <class \'__main__.Mammal\'>, <class \'__main__.Animal\'>, <class \'object\'>)

从 Cat 的 MRO 可以看到

  • CannotSwim 后面跟的是CannotFly 而不是 Mamal
  • 所以CannotSwim 类里面的 super() 会调用CannotFly 里面的方法

多继承的栗子二

实际代码

class A:def __init__(self):self.n = 2def add(self, m):# 第四步# 来自 D.add 中的 super# self == d, self.n == d.n == 5print(\'self is {0} @AAA.add\'.format(self))self.n += m# d.n == 7class C(A):def __init__(self):self.n = 4def add(self, m):# 第三步# 来自 B.add 中的 super# self == d, self.n == d.n == 5print(\'self is {0} @CCC.add\'.format(self))# 等价于 suepr(C, self).add(m)# self 的 MRO 是 [D, B, C, A, object]# 从 C 之后的 [A, object] 中查找 add 方法super().add(m)# 第五步# d.n = 7self.n += 4# d.n = 11class B(A):def __init__(self):self.n = 3def add(self, m):# 第二步# 来自 D.add 中的 super# self == d, self.n == d.n == 5print(\'self is {0} @BBB.add\'.format(self))# self 的 MRO 是 [D, B, C, A, object]# 从 B 之后的 [C, A, object] 中查找 add 方法# 从 C 找 add 方法super().add(m)# 第六步# d.n = 11self.n += 3# d.n = 14class D(B, C):def __init__(self):self.n = 5def add(self, m):# 第一步print(\'self is {0} @DDD.add\'.format(self))# self 的 MRO 是 [D, B, C, A, object]# 从 D 之后的 [B, C, A, object] 中查找 add 方法# 从 B 找 add 方法super().add(m)# 第七步# d.n = 14self.n += 5# self.n = 19d = D()d.add(2)print(d.n)

先看看 D 类的 MRO

print(D.__mro__)(<class \'__main__.D\'>, <class \'__main__.B\'>, <class \'__main__.C\'>, <class \'__main__.A\'>, <class \'object\'>)

输出结果

self is <__main__.D object at 0x10c14a190> @DDD.addself is <__main__.D object at 0x10c14a190> @BBB.addself is <__main__.D object at 0x10c14a190> @CCC.addself is <__main__.D object at 0x10c14a190> @AAA.add19

调用顺序的确是 D、B、C、A

执行顺序

class D(B, C):          class B(A):            class C(A):             class A:def add(self, m):       def add(self, m):      def add(self, m):       def add(self, m):super().add(m)  1.--->  super().add(m) 2.--->  super().add(m)  3.--->  self.n += mself.n += 5   <------6. self.n += 3    <----5. self.n += 4     <----4. <--|(14+5=19)               (11+3=14)              (7+4=11)                (5+2=7)

执行顺序图

super() 的注意事项

https://www.cnblogs.com/poloyy/p/15236135.html

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Python – 面向对象编程 – super()