python面向对象--继承

python面向对象–继承(MRO)

最近觉得py的基础还是有必要再看一下的。

python的多继承的属性继承搜索

Python在处理多继承时同名函数,如果处理呢?


Ex

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/ben/env python
#coding:utf-8
# __metaclass__ = type
class A(object):
def foo(self):
print "A-foo"
def bar(self):
print "A-bar"
class B(object):
def foo(self):
print "B-foo"
def bar(self):
print "B-bar"
class AA(A):
def bar(self):
print "AA-bar"
class BB(B):
def foo(self):
print "BB-bar"
class C(AA, BB):
pass
c = C()
c.foo()
c.bar()
class P1:
def foo(self):
print "p1-foo"
class P2:
def foo(self):
print "p2-foo"
def bar(self):
print "p2-bar"
class C1(P1, P2):
pass
class C2(P1, P2):
def bar(self):
print "C2-bar"
class D(C1, C2):
pass
print D.__mro__
d = D();
d.foo()
d.bar()
cc = CC()
cc.show()

在经典类中输出如下:output

1
2
3
4
A-foo
AA-bar
p1-foo
p2-bar

在新类中输出如下:output

1
2
3
4
A-foo
AA-bar
p1-foo
C2-bar

很明显的是,在老式的py类(py2.3以前的版本或者在py2.3+之后的版本中没有显示声明)中,依据的是从左到右,深度优先的规则.然而在新式类中,采用的是另外一套规则.所以在代码中混合新式类和旧式类,在MRO中会有不用的表现

类继承

使用类的__mro__属性可以得到一个可读的查找顺序表

mro即method resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类)。python在2.3中使用的是C3算法.

python 需要对其进行线性化(C3 Linearization),将继承图关系线性化。通过线性化,再依次查找类方法,直至找到该方法为止。线性化算法是python多继承的核心部分。线性化过程中,必须满足两个性质:

  • 单调性

  • 一致性
    直接父类的顺序通过用户来声明,父类线性化的顺序为从左到右。在进行线性化归并过程中,一致性主要保证类的局部优先级顺序,它定义了两个变量:

python多重继承

python在多重继承中的问题?
来看<>中的栗子

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
#!/usr/ben/env python
#coding:utf-8
# __metaclass__ = type
class Brid:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print "Aaaah..."
self.hungry = False
else:
print 'No, thanks'
class SongBrid(Brid):
def __init__(self):
self.sound = 'Squawk'
def sing(self):
print self.sound
sb = SongBrid()
sb.sing()
sb.eat()

结果报错,输出如下:

1
2
3
4
5
6
7
Squawk
Traceback (most recent call last):
File "inherit_2.py", line 25, in <module>
sb.eat()
File "inherit_2.py", line 10, in eat
if self.hungry:
AttributeError: SongBrid instance has no attribute 'hungry'

why?

因为在SongBrid中,构造方法被重写.所以找不到属性了

解决方法:

  • 1:调用未绑定的超类构造方法.
1
2
3
4
5
6
class SongBrid(Brid):
def __init__(self):
Brid.__init__(self)
self.sound = 'Squawk'
def sing(self):
print self.sound
  • 2:使用super函数(新式类才可以这么做)
1
2
3
4
5
6
lass SongBrid(Brid):
def __init__(self):
super(SongBrid, self).__init__()
self.sound = 'Squawk'
def sing(self):
print self.sound

super()类还是方法?

打印了以下super()的类型,发现类型是

Super的缺陷

基类中的__init__不会被显示调用,所以需要开发人员调用.

混用super和传统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
#coding:utf-8
class A(object):
def __init__(self):
print "A"
super(A, self).__init__()
class B(object):
def __init__(self):
print "B"
super(B, self).__init__()
class C(A, B):
def __init__(self):
print "C"
A.__init__(self) # 传统调用
B.__init__(self)
print "MRO:", [x.__name__ for x in C.__mro__]
C()

output:

1
2
3
4
5
MRO: ['C', 'A', 'B', 'object']
C
A
B
B

可以看到,当C实例调用A.__init__(self),因为super(A, self).init()将调用B的构造程序.

不同类型的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class BaseBase(object):
def __init__(self):
print "basebase"
super(BaseBase, self).__init__()
class Base1(BaseBase):
def __init__(self):
print "base1"
super(Base1, self).__init__()
class Base2(BaseBase):
def __init__(self, arg):
print 'base2'
super(Base2, self).__init__(arg)
class MyClass(Base1, Base2):
def __init__(self):
print "my base"
super(MyClass, self).__init__()
m = MyClass(10)

一种解决方式,就是给所有的init(self), 替换成init(self, args, *kw)


最佳实践

  • 减少使用多继承
  • super不能混用
  • 检查MRO
坚持技术分享,您的支持将鼓励我继续创作!