调用父类的初始化方法
继承父类后,直接调用父类的初始化方法。
定义一个人的类Person
,包含人的名字和年龄,学生类Student
继承自Person
,多了一个分数变量:
class Person():
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print("Person\'s name is {}, person\'s age is {}"
.format(self.name, self.age))
class Student(Person):
def __init__(self, name, age, score):
super().__init__(name, age)
self.score = score
def info(self):
print("Student\'s name is {}, age is {}, score is {}"
.format(self.name, self.age, self.score))
p1 = Person("Zhangsan", 20)
p1.info()
>>> Person's name is zhangsan, person's age is 20
s1 = Student("Lisi", 18, 100)
s1.info()
>>> Student's name is Lisi, age is 18, score is 100
同时实现父类功能
如上一个示例,在Student
类中重定义info
方法直接就覆盖了父类Person
中的info
方法,如果需要同时实现父类的功能,就用super
调用父类中的方法。只需要在Student
中简单的修改下就可以:
class Student(Person):
def __init__(self, name, age, score):
super().__init__(name, age)
self.score = score
def info(self):
super().info()
print("Student\'s score is {}".format(self.score))
s1 = Student("Lisi", 18, 100)
s1.info()
>>> Person's name is Lisi, person's age is 18
>>> Student's score is 100
使用super
避免了直接用父类的名去调用,方便修改。 在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。
多重继承
上面说到的例子是单继承,用父类名.属性
的方法调用出来代码维护时繁琐一点也并无不可,但多重继承时,还用这种方法来调用父类属性就会就会带来许多问题。假如有以下4个类,箭头指向父类,要在各子类方法中显示调用父类:
class Base():
def fun(self):
print("enter Base")
print("leave Base")
class A(Base):
def fun(self):
print("enter A")
Base.fun(self)
print("leave A")
class B(Base):
def fun(self):
print("enter B")
Base.fun(self)
print("leave B")
class C(A, B):
def fun(self):
print("enter C")
A.fun(self)
B.fun(self)
print("leave C")
c = C()
c.fun()
>>> enter C
>>> enter A
>>> enter Base
>>> leave Base
>>> leave A
>>> enter B
>>> enter Base
>>> leave Base
>>> leave B
>>> leave C
Base
类被实例化了两次,发生菱形继承,出现调用不明确问题,并且会造成数据冗余的问题 。
MRO列表
对于定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,类C
的MRO表:
C.mro()
>>> [__main__.C, __main__.A, __main__.B, __main__.Base, object]
MRO 列表通过C3线性化算法
来实现的,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
- 子类永远在父类前面
- 如果有多个父类,会根据它们在列表中的顺序被检查。
- 如果对下一个类存在两个合法的选择,选择第一个父类。
当用super调用父类的方法时,会按照mro表中的元素顺序去挨个查找方法。
class Base():
def fun(self):
print("enter Base")
print("leave Base")
class A(Base):
def fun(self):
print("enter A")
super().fun()
print("leave A")
class B(Base):
def fun(self):
print("enter B")
super().fun()
print("leave B")
class C(A, B):
def fun(self):
print("enter C")
super().fun()
print("leave C")
c = C()
c.fun()
>>> enter C
>>> enter A
>>> enter B
>>> enter Base
>>> leave Base
>>> leave B
>>> leave A
>>> leave C
为什么继承顺序是 A → B → B a s e A \rightarrow B \rightarrow Base A→B→Base ?
super原理
super的工作原理如下:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls
代表类,inst
代表实例,上面的代码做了两件事:
- 获取
inst
的MRO列表 - 查找
cls
在当前MRO列表中的index, 并返回它的下一个类,即mro[index + 1]
当你使用 super(cls, inst)
时,Python 会在inst
的 MRO 列表上搜索cls
的下一个类。
所以根据MRO表,类C
的下一个类是A
,以此类推,得到
A
→
B
→
B
a
s
e
A \rightarrow B \rightarrow Base
A→B→Base 的继承顺序。