参考:
Python构造函数使用
Python中的构造方法(__init__()
与__new__()
)
super()的两个参数
【精】python之理解super
Python
可以继承多个父类。
Python
不同于Java
(只能继承一个父类)。
一段简单的多继承例子如下:
S
↗ ↑ ↖
A B C
↖ ↑ ↗
D
class S(object):
def __init__(self) -> None:
pass
class A(S):
def __init__(self) -> None:
pass
class B(S):
def __init__(self) -> None:
pass
class C(S):
def __init__(self) -> None:
pass
class D(A,B,C):
def __init__(self) -> None:
pass
__init__
与__new__
__new__(cls,*arg,**kwarg) -> Any
用于 对象的创建。
参数
cls
:指明需要创建的类class。该参数在实例化时,由Python
解释器自动提供。
至少需要一个参数,变量名字可以变化。
功能: 在__new__
中创建类class,并返回return
。
在重写__new__
方式时,调用使用super()
调用父类的__new__
方法即可创建对象。
class D(object):
def __init__(self) -> None:
pass
def __new__(cls) -> Any:
return super().__new__(cls)
__init__(self,*arg,**kwarg) -> None
用于 对象的初始化。
参数
self
:为__new__()
返回的实例。同为Python
解释器自动提供。
至少需要一个参数,变量名字可以变化。
功能: 在__init__
中完成 类成员的初始化等,无返回值。
super
在Python2.2
之后,引入了super
来优化调用"父类"函数的逻辑。
但是对于多继承的情况,class D(A,B,C)
,调用super()
之后,到底是调用哪一个父类呢?
这里,先引入MRO
表的概念。
MRO
表简单地来说,MRO(Method Resolution Order)
代表了类的继承顺序。
例如,一段多继承关系如下:
S
↗ ↑ ↖
A B C
↖ ↑ ↗
D
print(D.mro()) # 获得MRO表
print(D.__mro__) # 获得MRO表
>>> [<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.S'>, <class 'object'>]
简单来说,一个MRO列表就是合并所有父类的列表,并遵循下面几点:
super(cls,)
cls
:类class
。指明 (我们调用的是)类cls
在mro
表中下一个类K
(的函数/成员变量)。
:对象实例
instance
。作为传入该类(K
)第一个参数,作为调用 函数/成员变量 的实例。一般为该对象实例self
。
考虑下面继承关系,
S' S2
↗ ↑ ↖ ↑
A' B' C A2
↖ ↑ ↗
D
其中,A,B,S有`call`方法,而其他的没有。
类D
、类A2
的MRO
表分别为:
>>> D -> A -> B -> C -> S -> object
>>> A2 -> S2 -> object
具体代码如下,
调用类D
“父类”的call()
方法。
class S(object):
def call(self):
print(f"S: {self}")
class A(S):
def call(self):
print(f"A: {self}")
class B(S):
def call(self):
print(f"B: {self}")
class C(S):
pass
class D(A,B,C):
def __init__(self):
print(f"D: {self}")
print("#"*20,"super(D,self).call(): ")
super(D,self).call()
print("#"*20,"super(A,self).call(): ")
super(A,self).call()
print("#"*20,"super(B,self).call(): ")
super(B,self).call()
print("#"*20,"super(C,self).call(): ")
super(C,self).call()
print("#"*20,"super(S,self).call(): ")
super(S,self).call()
X = D()
此时,输出为
D: <__main__.D object at 0x00000172F5CF56A0>
#################### super(D,self).call():
A: <__main__.D object at 0x00000172F5CF56A0>
#################### super(A,self).call():
B: <__main__.D object at 0x00000172F5CF56A0>
#################### super(B,self).call():
S: <__main__.D object at 0x00000172F5CF56A0>
#################### super(C,self).call():
S: <__main__.D object at 0x00000172F5CF56A0>
#################### super(S,self).call():
AttributeError: 'super' object has no attribute 'call'
解释:
当调用super(D,self).call()
时,
实际上,我们调用了在MRO
表中,D
的下一个类(A
)的call()
方法。
当调用super(B,self).call()
时,
B
的下一个类(C
)是没有call
方法的,所以Python
会继续查找下一个有call()
方法的类(S
)。
当调用super(S,self).call()
时,
S
的下一个类object
没有call()
方法,因为MRO
表中,已经到尽头了,所以抛出异常。
注意到,调用
call()
的都是D
的实例。
考虑,另外一种情况
class S2(object):
def __init__(self) -> None:
print(f"S2: {self}")
super(S2,self).__init__()
class A2(S2):
def __init__(self) -> None:
print(f"A2: {self}")
super(A2,self).__init__()
########################################
... A,B,C,S 代码同上
class D(A,B,C):
def __init__(self):
print(f"D: {self}")
print("#"*20,"a2 = A2():")
a2 = A2()
print("#"*20,"super(A2,a2):")
super(A2,a2).__init__()
print("#"*20)
X = D()
输出为,
D: <__main__.D object at 0x0000027D885C56A0>
#################### a2 = A2():
A2: <__main__.A2 object at 0x0000011227295FA0>
S2: <__main__.A2 object at 0x0000011227295FA0>
#################### super(A2,a2):
S2: <__main__.A2 object at 0x0000011227295FA0>
####################
解释:
在类
D
中,也可以调用A2
的“父类”(S2
)的方法。
在a2 = A2()
中,先调用A2
的__init__()
方法,然后,在调用S2
的__init__()
方法。
在super(A2,a2).__init__()
中,调用的是 在MRO
表中,A2
的下一个类(S2
)的__init__()
方法。
注意到,调用
__init__()
方法都是A2
的实例(即a2
)。
在Java
中,super
足够明确父类。而在Python
中,单单super
不能明确“父类”,因此还需要引入两个参数。
在Python
中,
super
与父类
没有实质性的关联。super(cls,instance).method()
,只不过都是 在查找MRO
表中 类cls
的的下一个类,然后 使用实例instance
调用method()
而已。特别地,
在Python3
中,super()
等同于super(cls,
。
此时,
cls
:为调用super()
时,所处的类class
。
:为调用
super()
时,函数的第一个参数,一般为self
。然后,这个参数作为调用方法的第一个参数(即对象实例)传递,在下面的例子中为__init__()
。
例子如下:
class D(object):
def __init__(self,a,b,c):
# 下面两种方法等同
super().__init__()
super(D,self).__init__()
# 同样的,也有
class D(object):
def __init__(x,a,b,c):
# 下面两种方法等同
super().__init__()
super(D,x).__init__()
构造方法
、 __init__()
、super
在Python
中,概念 构造方法
与 __init__()
并不相同。
在Java
中,
public class B extends A{
B(){
//super()
}
}
super
语句,一定在 构造方法第一行执行。
构造函数
没有定义,Java
会提供默认的无参构造函数。super
没有显式调用,Java
会默认调用父类的无参构造方法super()
,并在构造方法的第一行执行。super()
没有在第一行执行,则会抛出异常。在Python
中,
class B(A):
def __init__(self):
super(B,self).__init__()
super
语句,不会一定执行。
__init__
,Python
会默认调用“父类”的构造函数,即super(B,self).__init__(*arg,**kwarg)
,对B
进行初始化。super
没有显式调用,Python
不会调用super
语句,即super(B,self).__init__(*arg,**kwarg)
不会执行。