super的简单用法
class Father(object):
def __init__(self, name):
self.name = name
class Son(Father):
def __init__(self, name, age):
# super(Son, self).__init__(name) 完整的调用方式,后面再详细说明
super().__init__(name) # 调用父类的初始化方法
self.age = age # 初始化本类参数
多继承中super的使用
class Father(object):
def __init__(self, name, *args):
self.name = name
super().__init__(*args)
class Mother(object):
def __init__(self, age, *args):
self.age = age
super().__init__(*args)
class Son(Father, Mother):
def __init__(self, name, age, gender):
super().__init__(name, age)
self.gender = gender
super简单的说是调用父类的方法,不管是父类的初始化方法还是实例方法都可以;但实质并非是这样,比如Father
类和Mother
类中的super并非都是调用object
的初始化方法,否则也达不到初始化Son
实例的效果。事实上这与MRO有关,当然你可能不会太明白上例的用法,请接着往下看。
关于MRO
MRO:method resolution order 是python类继承方法或属性的解析顺序即寻找顺序,示例如下:
class Father(object):
def __init__(self, name, *args):
self.name = name
super().__init__(*args)
class Mother(object):
def __init__(self, age, *args):
self.age = age
super().__init__(*args)
class Son(Father, Mother):
def __init__(self, name, age, gender):
super().__init__(name, age)
self.gender = gender
if __name__ == '__main__':
print(Son.__mro__)
# result: (<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)
打印 mro
,结果是一个类名组成的元组,类名在元组中的顺序就是python解释器寻找方法或属性时查找的顺序。
正如前文所述,调用父类初始化函数完整的方式是使用super(Son, self).__init__(name)
,super中第一个参数传递Son
即表示调用mro
元组中类名为Son
后面紧邻类的初始化方法即Father
类的初始化方法;若不传递参数则默认传递当前类名,所以可以简写为super().__init__(name)
。因此使用super实质上调用的是在mro
元组中排列在当前类的后面类中的方法。
如此一来我们就可以解释多继承中为啥要这样使用super:首先在Son
类中调用super传递两个初始化参数,由于没有指定类名,所以默认为当前类名,super().__init__(name, age)
会按照mro
的顺序调用 Father
中的方法,初始化name
;接着多余的参数会通过*args
传递给Father
类的super方法,此时按照mro
顺序会调用Mother
类的初始化方法,初始化age
。之后已经没有多余的参数了,*args
为空元组,由于在Mother
中调用了super所以还是会执行object
类的初始化方式,不过并不会产生任何影响。此时Son
类的实例初始化完毕。
当然正如我们之前所说的那样,super中传递的类名表示调用此类名在mro
元组中后面的类中的方法,所以上面的例子可以这样改写:
class Father(object):
def __init__(self, name):
self.name = name
class Mother(object):
def __init__(self, age):
self.age = age
class Son(Father, Mother):
def __init__(self, name, age, gender):
super(Son, self).__init__(name) # 调用Father的初始化方法
super(Father, self).__init__(age) # 调用Mother的初始化方法
self.gender = gender
这样也可以完成初始化的工作,减少了在父类中传递不定参数的麻烦,前提是得知道mro
的解析顺序,否则容易传递错初始化参数。