调用父类方法

调用父类方法

super()

  1. 为了调用父类(超类)的一个方法,可以使用 super() 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A:
def spam(self):
print('A.spam')

class B(A):
def spam(self):
print('B.spam')
super().spam() # Call parent spam()

b = B()
b.spam()
'''
输出结果
B.spam 先调用 B 的 spam
A.spam 然后再调用 A 的 spam
'''
  1. super() 函数的一个常见用法是在 __init__() 方法中确保父类被正确的初始化了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class A:
    def __init__(self):
    self.x = 0

    class B(A):
    def __init__(self):
    super().__init__()
    self.y = 1

    b = B()
    print(b.x,b.y) # 0,1

使用dataclasses改写

1
2
3
4
5
6
7
8
9
10
11
12
from dataclasses import dataclass

@dataclass
class A:
x:int = 0

@dataclass
class B(A):
y:int = 1

b = B()
print(b) # B(x=0, y=1)

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Father:
    def __init__(self, name):
    self.name = name
    print("init Father's name")


    class Mather:
    def __init__(self, age):
    self.age = age
    print("init Mather's age")


    class Son(Father, Mather):
    def __init__(self, name, age, sex):
    super().__init__(name) # 先继承 Father 的 name
    super(Father, self).__init__(age) # 继承后 再继承 Mother 的age
    self.sex = sex # 最后 实例化自己的 sex
    print("init Son's sex")


    if __name__ == "__main__":
    son = Son("Tom", 5, "Male")
    print(Son.__mro__) # (<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mather'>, <class 'object'>)
    print(son.name, son.age, son.sex)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from dataclasses import dataclass

@dataclass
class Father:
name:str
print("init Father's name")

@dataclass
class Mather:
age:int
print("init Mather's age")

@dataclass
class Son(Father, Mather):
sex:str
print("init Son's sex")


if __name__ == "__main__":
son = Son(name="Tom", age=5, sex="Male") # 这个会先实例化age,再name,再sex
print(Son.__mro__) # (<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mather'>, <class 'object'>)
print(son)

MRO列表

Python会在MRO列表上继续搜索下一个类。
只要每个重定义的方法统一使用 super() 并只调用它一次,
那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次
super()有个令人吃惊的地方是它并不一定去查找某个类在MRO中下一个直接父类,
你甚至可以在一个没有直接父类的类中使用它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A:
def spam(self):
print('A.spam')
super().spam()
class B:
def spam(self):
print('B.spam')

class C(A,B):
pass

c = C()
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
c.spam()
'''
A.spam
B.spam
'''
1
2
3
4
5
6
7
8
9
10
class C(B,A):
pass

c = C()
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
c.spam()
'''
B.spam
'''
# 不会报错,只会执行 B下面的

关于super()

由于 super() 可能会调用不是你想要的方法,你应该遵循一些通用原则。
首先,确保在继承体系中所有相同名字的方法拥有可兼容的参数签名(比如相同的参数个数和参数名称)。
这样可以确保 super() 调用一个非直接父类方法时不会出错。
其次,最好确保最顶层的类提供了这个方法的实现,这样的话在MRO上面的查找链肯定可以找到某个确定的方法。

在Python社区中对于 super() 的使用有时候会引来一些争议。
尽管如此,如果一切顺利的话,你应该在你最新代码中使用它。
Raymond Hettinger为此写了一篇非常好的文章
Python’s super() Considered Super
通过大量的例子向我们解释了为什么 super() 是极好的。