多继承
除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。
多继承格式
1
2
|
class 子类(父类1,父类2,...):
pass
|
例如,下面类C继承类A和类B。
1
2
3
4
5
6
7
8
|
class A:
pass
class B:
pass
class C(A,B):
pass
|
多继承MRO问题
MRO即method resolution order,方法解析顺序,用于判断子类调用的属性来自于哪个父类。类C继承自父类A和B,调用C的属性时,是调用A的属性还是B的属性。通过类的mro属性,可以解决此问题。
mro属性
此属性是在方法解析期间查找基类的元组。此属性引入的目的主要是为了解决查找顺序(MRO)的问题。
1
2
3
4
5
6
7
8
9
10
11
12
|
class A:
pass
class B:
pass
class C(A,B):
pass
print(A.__mro__)
print(B.__mro__)
print(C.__mro__)
|
执行以上程序会输出如下结果:
1
2
3
|
(<class '__main__.A'>, <class 'object'>)
(<class '__main__.B'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
|
从上面可以看出,类C首先查找本身,如果没有的话,再查找父类A,没有的话,查找父类B,最后查找object。
例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class A:
attrA = "Attr A"
name = "Class Name"
class B:
attrB = "Attr B"
class C(A,B):
attrC = "Attr C"
C.attrB #首先在类C本身查找属性attrB。不存在,接着在类A中查找属性attrB。也不存在,在类B中查找属性attrB,找到了
C.attrA #首先在类C本身查找属性attrA。不存在,接着在类A中查找属性attrA。找到了
C.attrC #首先在类C本身查找属性attrC,找到了
C.name #首先在类C本身查找属性name。不存在,接着在类A中查找属性name。找到了
|
base属性
此属性用来记录类的父类。有多个父类时,返回第一个。
1
2
3
4
5
6
7
8
9
10
11
12
|
class A:
pass
class B:
pass
class C(A,B):
pass
print(A.__base__) # 打印A的父类
print(B.__base__) # 打印B的父类
print(C.__base__) # 打印C的父类
|
执行以上程序会输出如下结果:
1
2
3
|
<class 'object'>
<class 'object'>
<class '__main__.A'>
|
如果C的类继承顺序更改为先B后A。
1
2
3
4
5
6
7
8
9
10
11
12
|
class A:
pass
class B:
pass
class C(B,A):
pass
print(A.__base__) #打印A的父类
print(B.__base__) #打印B的父类
print(C.__base__) #打印C的父类
|
执行以上程序会输出如下结果:
1
2
3
|
<class 'object'>
<class 'object'>
<class '__main__.B'>
|
bases
此属性用来记录类对象的基类的元组。
1
2
3
4
5
6
7
8
9
10
11
12
|
class A:
pass
class B:
pass
class C(A,B):
pass
print(A.__bases__)
print(B.__bases__)
print(C.__bases__)
|
执行以上程序会输出如下结果:
1
2
3
|
(<class 'object'>,)
(<class 'object'>,)
(<class '__main__.A'>, <class '__main__.B'>)
|
如果C的类继承顺序更改为先B后A。
1
2
3
4
5
6
7
8
9
10
11
12
|
class A:
pass
class B:
pass
class C(B,A):
pass
print(A.__bases__)
print(B.__bases__)
print(C.__bases__)
|
执行以上程序会输出如下结果:
1
2
3
|
(<class 'object'>,)
(<class 'object'>,)
(<class '__main__.B'>, <class '__main__.A'>)
|
多继承钻石问题(也叫菱形问题)
如果子类继承自两个单独的父类,而那两个父类又继承自同一个公共基类,那么就构成了钻石继承体系。这种继承体系很像竖立的菱形,也称作菱形继承。
插入图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class A:
def __init__(self, value):
self.value = value
class B(A):
def __init__(self, value):
A.__init__(self, value)
self.value += 2
class C(A):
def __init__(self, value):
A.__init__(self, value)
self.value += 3
class D(B, C):
def __init__(self, value):
B.__init__(self, value)
C.__init__(self, value)
d = D(1)
print(d.value) #结果是4,而不是6
|
为啥结果不是6呢。因为在调用第二个超类时,即C.__init__,它会再次调用A.__init__,从而导致self.value重新变成1。
解决这个问题的方式是使用super方法,根据方法解析顺序(MRO)以标准化的流程来安排超类之间的初始化顺序,它也能够保证钻石顶部的公共基类的init方法之运行一次。
修改如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class A:
def __init__(self, value):
self.value = value
class B(A):
def __init__(self, value):
super().__init__(value + 2)
class C(A):
def __init__(self, value):
super().__init__(value + 3)
class D(B, C):
def __init__(self, value):
super().__init__(value)
d = D(1)
print(d.value) # 结果6
|
super()函数和init()函数
既然super()函数和init()函数都是初始化类的,他们之间有什么异同呢。
相同点
都是用于初始化类的,单继承时super()和init()实现的功能是类似的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Base:
def __init__(self):
print('Base create')
class childA(Base):
def __init__(self):
print('creat A')
Base.__init__(self)
class childB(Base):
def __init__(self):
print('creat B')
super().__init__()
base = Base()
a = childA()
b = childB()
|
执行以上程序会输出如下结果:
1
2
3
4
5
|
Base create
creat A
Base create
creat B
Base create
|
不同点
super是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。多重继承请使用super().xxx。
语法
super(type[, object-or-type])
参数
type – 类。
object-or-type – 类,一般是self
Python3.x和Python2.x的一个区别是: Python3直接使用super().xxx 代替super(Class, self).xxx。
Python3.x 实例:
1
2
3
4
5
|
class A:
pass
class B(A):
def add(self, x):
super().add(x)
|
Python2.x 实例:
1
2
3
4
5
|
class A(object): # Python2.x 记得继承 object
pass
class B(A):
def add(self, x):
super(B, self).add(x)
|
转载请注明本网址。