咱们常常在类的继承当中使用super(), 来调用父类中的方法。例以下面:html
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
A:
def
func(
self
):
print
(
'OldBoy'
)
class
B(A):
def
func(
self
):
super
().func()
print
(
'LuffyCity'
)
A().func()
B().func()
|
输出的结果为:python
OldBoy
OldBoy
LuffyCity
A实例化的对象调用了func方法,打印输出了 Oldboy;函数
B实例化的对象调用了本身的func方法,先调用了父类的方法打印输出了 OldBoy ,再打印输出 LuffyCity 。post
这样是Python3的写法,今天我们也只讨论Python3中的super。测试
若是不使用super的话,想获得相同的输出截个,还能够这样写B的类:ui
1
2
3
4
|
class
B(A):
def
func(
self
):
A.func(
self
)
print
(
'LuffyCity'
)
|
这样能实现相同的效果,只不过传了一个self参数。那为何还要使用super()呢?spa
那我看看有这样的一个继承关系的类(钻石继承):代理
1
2
3
4
5
6
7
|
Base
/
\
/
\
A B
\
/
\
/
C
|
代码是这样的:code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
Base:
def
__init__(
self
):
print
(
'Base.__init__'
)
class
A(Base):
def
__init__(
self
):
Base.__init__(
self
)
print
(
'A.__init__'
)
class
B(Base):
def
__init__(
self
):
Base.__init__(
self
)
print
(
'B.__init__'
)
class
C(A, B):
def
__init__(
self
):
A.__init__(
self
)
B.__init__(
self
)
print
(
'C.__init__'
)
C()
|
输出的结果是:htm
Base.__init__ A.__init__ Base.__init__ B.__init__ C.__init__
每一个子类都调用父类的__init__方法,想把全部的初始化操做都作一遍,可是出现了一个问题,Base类的__init__方法被调用了两次,这是多余的操做,也是不合理的。
那咱们改写成使用super()的写法:
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
Base:
def
__init__(
self
):
print
(
'Base.__init__'
)
class
A(Base):
def
__init__(
self
):
super
().__init__()
print
(
'A.__init__'
)
class
B(Base):
def
__init__(
self
):
super
().__init__()
print
(
'B.__init__'
)
class
C(A, B):
def
__init__(
self
):
super
().__init__()
print
(
'C.__init__'
)
C()
|
输出的结果是:
Base.__init__ B.__init__ A.__init__ C.__init__
这样执行的结果就比较满意,是大多数人想要的结果。那为何会是这样的结果呢?
那是由于咱们每定义一个类的时候,Python都会建立一个MRO列表,用来管理类的继承顺序。
1
2
|
print
(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
|
Python经过这个列表从左到右,查找继承的信息。Python3中的类都是新式类,都有这个mro属性,能看出来是广度优先的查找原则。经典类就没有mro属性,但它的查找原则是深度优先。
那我回到super的问题上来,让咱们先看看super的官方定义。
super([type[, object-or-type]])
返回一个代理对象,该对象将方法调用委托给类的父类或兄弟类。这对于访问类中已重写的继承方法很是有用。搜索顺序与getattr()使用的搜索顺序相同,只是类型自己被跳过。
类的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。属性是动态的,能够在继承层次结构更新时进行更改。
看到官方的解释就能够很清楚的明白,super是一个类,实例化以后获得的是一个代理的对象,而不是获得了父类,而且咱们使用这个代理对象来调用父类或者兄弟类的方法。
那咱们再看看super的使用方法:
super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
super至少须要一个参数,而且类型须要是类。
不传参数的会报错。只传一个参数的话是一个不绑定的对象,不绑定的话也就没什么用了。
1
2
|
print
(
super
(C))
print
(
super
())
|
输出结果:
RuntimeError: super(): no arguments <super: <class 'C'>, NULL>
在定义类当中能够不写参数,Python会自动根据状况将两个参数传递给super。
1
2
3
4
5
6
7
8
|
class
C(A, B):
def
__init__(
self
):
print
(
super
())
super
().__init__()
print
(
'C.__init__'
)
C()
|
输出结果:
<super: <class 'C'>, <C object>> Base.__init__ B.__init__ A.__init__ C.__init__
因此咱们在类中使用super的时候参数是能够省略的。
第三种用法, super(type, obj) 传递一个类和对象,获得的是一个绑定的super对象。这还须要obj是type的实例,能够不是直接的实例,是子类的实例也行。
1
2
3
|
a
=
A()
print
(
super
(A, a))
print
(
super
(Base, a))
|
输出结果:
Base.__init__ A.__init__ <super: <class 'A'>, <A object>> <super: <class 'Base'>, <A object>>
第三种用法, super(type, type2)传递两个类,获得的也是一个绑定的super对象。这须要type2是type的子类。
1
2
3
|
print
(
super
(Base, A))
print
(
super
(Base, B))
print
(
super
(Base, C))
|
输出结果:
<super: <class 'Base'>, <A object>> <super: <class 'Base'>, <B object>> <super: <class 'Base'>, <C object>>
接下来咱们就该说说查找顺序了,两个参数,是按照那个参数去计算MRO呢?
咱们将C类中的super的参数填写上,而且实例化,看看输出的结果。
1
2
3
4
|
class
C(A, B):
def
__init__(
self
):
super
(C,
self
).__init__()
print
(
'C.__init__'
)
|
输出结果:
Base.__init__ B.__init__ A.__init__ C.__init__
看结果和以前super没填参数的结果是同样的。
那咱们将super的第一个参数改成A:
1
2
3
4
|
class
C(A, B):
def
__init__(
self
):
super
(A,
self
).__init__()
print
(
'C.__init__'
)
|
输出结果:
Base.__init__
B.__init__
C.__init__
咦!?那A.__init__怎么跑丢了呢?多出来了B.__init__呢?
这是应为Python是按照第二个参数来计算MRO,此次的参数是self,也就是C的MRO。在这个顺序中跳过一个参数(A)找后面一个类(B),执行他的方法。
知道这个后,输出的结果就能够理解了。 super(A, self).__init__() 没有执行Base的方法,而是执行了B的方法。
那咱们接下来讲说 super(type, obj) 和 super(type, type2)的区别。
代码以下:
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
Base:
def
func(
self
):
return
'from Base'
class
A(Base):
def
func(
self
):
return
'from A'
class
B(Base):
def
func(
self
):
return
'from B'
class
C(A, B):
def
func(
self
):
return
'from C'
c_obj
=
C()
print
(
super
(C, C))
print
(
super
(C, c_obj))
|
输出结果:
<super: <class 'C'>, <C object>> <super: <class 'C'>, <C object>>
两次的打印结果如出一辙,verygood。那他们的方法是不是同样的呢?测试一下。
1
2
|
print
(
super
(C, C).func
is
super
(C, c_obj).func)
print
(
super
(C, C).func
=
=
super
(C, c_obj).func)
|
输出结果:
False
False
他俩的方法既不是指向同一个,值还不相等。是否是搞错了呢?再试试下面的看看。
1
2
3
4
5
6
|
c1
=
super
(C, C)
c2
=
super
(C, C)
print
(c1
is
c2)
print
(c1
=
=
c2)
print
(c1.func
is
c2.func)
print
(c1.func
=
=
c2.func)
|
输出结果:
False
False
True
True
c1和c2不是一个对象,可是他们的方法倒是相同的。
那 super(C, C).func 和 super(C, c_obj).func 的确是不一样的。那打印出来看看有什么区别:
1
2
|
print
(
super
(C, C).func)
print
(
super
(C, c_obj).func)
|
输出结果:
<function A.func at 0x0000000009F4D6A8>
<bound method A.func of <__main__.C object at 0x00000000022A94E0>>
super的第二个参数传递的是类,获得的是函数。
super的第二个参数传递的是对象,获得的是绑定方法。
函数和绑定方法的区别就再也不赘述了,在这里想获得同样的结果,只须要给函数传递一个参数,而绑定方法则不须要传递额外的参数了。
1
2
|
print
(
super
(C, C).func(c_obj))
print
(
super
(C, c_obj).func())
|
输出结果:
from A from A
那我如今总结一下:
- super()使用的时候须要传递两个参数,在类中能够省略不写,咱们使用super()来找父类或者兄弟类的方法;
- super()是根据第二个参数来计算MRO,根据顺序查找第一个参数类后的方法。
- super()第二个参数是类,获得的方法是函数,使用时要传self参数。第二个参数是对象,获得的是绑定方法,不须要再传self参数。
给使用super()的一些建议:
- super()调用的方法要存在;
- 传递参数的时候,尽可能使用*args 与**kwargs;
- 父类中的一些特性,好比【】、重写了__getattr__,super对象是不能使用的。
- super()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。