说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的内置函数 super() 用于调用父类(超类)的一个方法,用来解决多重继承问题的。不仅仅可以调用父类的构造函数,还可以调用父类的成员函数。
super() 内置函数(对象)返回一个代理对象(超类的临时对象),允许我们访问基类的方法。
class Person(object):
def eat(self, times):
print(f'我每天吃{times}餐。')
class Student(Person):
def eat(self):
# 调用超类
super().eat(4)
tom = Student()
tom.eat()
# 我每天吃4餐。
Student.mro()
# [__main__.Student, __main__.Person, object]
调用父类初始化继承:
class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b
class A(Base):
def __init__(self, a, b, c):
super().__init__(a, b)
super(A, self).__init__(a, b) # Python2 写法
self.c = c
a = A(1,2,3)
A.mro()
# [__main__.A, __main__.Base, object]
它其实是一个内置的类,语法如下:
class super(self, /, *args, **kwargs)
# or
super(type[, object-or-type])
调用形式可以有以下几种:
super() # 同 super(__class__, <first argument>)
super(type) # 未绑定超类对象
super(type, obj) # 绑定超类对象; 实现 isinstance(obj, type)
super(type, type2) # 绑定超类对象; 实现 issubclass(type2, type)
调用超类方法的典型用法:
class C(B):
def meth(self, arg):
super().meth(arg)
也适用于类方法:
class C(B):
@classmethod
def cmeth(cls, arg):
super().cmeth(arg)
super() 返回一个代理对象,它会将方法调用委托给 type 的父类或兄弟类。 这对于访问已在类中被重载的继承方法很有用。
object-or-type 确定用于搜索的 MRO (method resolution order)。 搜索会从 type 之后的类开始。
举例来说,如果 object-or-type 的 __mro__
为 D -> B -> C -> A -> object 并且 type 的值为 B,则 super() 将会搜索 C -> A -> object。
object-or-type 的 __mro__
属性列出了 getattr() 和 super() 所共同使用的方法解析搜索顺序。 该属性是动态的,可以在任何继承层级结构发生更新的时候被改变。
如果省略第二个参数,则返回的超类对象是未绑定的。 如果第二个参数为一个对象,则 isinstance(obj, type)
必须为真值。 如果第二个参数为一个类型,则 issubclass(type2, type) 必须为真值(这适用于类方法)。
super 有两个典型用例。 在具有单继承的类层级结构中,super 可用来引用父类而不必显式地指定它们的名称,从而令代码更易维护。 这种用法与其他编程语言中 super 的用法非常相似。
第二个用例是在动态执行环境中支持协作多重继承。 此用例为 Python 所独有而不存在于静态编码语言或仅支持单继承的语言当中。 这使用实现“菱形图”成为可能,即有多个基类实现相同的方法。 好的设计强制要求这样的方法在每个情况下都具有相同的调用签名(因为调用顺序是在运行时确定的,也因为这个顺序要适应类层级结构的更改,还因为这个顺序可能包括在运行时之前未知的兄弟类)。
对于以上两个用例,典型的超类调用看起来是这样的:
class C(B):
def method(self, arg):
super().method(arg) # This does the same thing as:
# super(C, self).method(arg)
除了方法查找之外,super() 也可用于属性查找。 一个可能的应用场合是在上级或同级类中调用 描述器。
请注意 super() 是作为显式加点属性查找的绑定过程的一部分来实现的,例如 super().__getitem__(name)
。 它做到这一点是通过实现自己的 __getattribute__()
方法,这样就能以可预测的顺序搜索类,并且支持协作多重继承。 对应地,super() 在像 super()[name] 这样使用语句或操作符进行隐式查找时则未被定义。
还要注意的是,除了零个参数的形式以外,super() 并不限于在方法内部使用。 两个参数的形式明确指定参数并进行相应的引用。 零个参数的形式仅适用于类定义内部,因为编译器需要填入必要的细节以正确地检索到被定义的类,还需要让普通方法访问当前实例。
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了。
事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表,以页案例为例:
Student.__mro__
Student().__class__.mro()
Student.mro()
# [__main__.Student, __main__.Person, object]
这个列表真实的列出了类C的继承顺序。Student->Person->object。在方法调用时,是按照这个顺序查找的。
那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,这里我们就不去深究这个算法了,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
super 的工作原理如下:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类,因此 super() 和父类没有实质性的关联。
比如在下列的菱形继承关系中,C 的 MRO 列表是 C->A->B->Base->object:
Base
/ \
/ \
A B
\ /
\ /
C
代码如:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
c = C()
'''
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
'''
C.mro()
# [__main__.C, __main__.A, __main__.B, __main__.Base, object]
enter A 的下一句不是 enter Base 而是 enter B,原因是,super 和父类没有实质性的关联,是按照 mro 顺序运行的。
因此,super() 的主要作用:
更新时间:2022-05-23 15:21:15 标签:python 类 父类 超类