Python继承及使用方法
什么是继承?
- 继承机制经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类。也就是说,通过使用继承这种机制,可以轻松实现类的重复使用。
派生和继承
- “派生”和“继承”是一个意思,只是观察角度不同而已。换句话话,继承是相对子类来说的,即子类继承自父类;而派生是相对于父类来说的,即父类派生出子类。
class People:
def __init__(self):
self.name = People
def say(self):
print("People类",self.name)
class Animal:
def __init__(self):
self.name = Animal
def say(self):
print("Animal类",self.name)
#People中的 name 属性和 say() 会遮蔽 Animal 类中的
class Person(People, Animal):
pass
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.say()
###运行结果
People类 张三
可以看到,当 Person 同时继承 People 类和 Animal 类时,People 类在前,因此如果 People 和 Animal 拥有同名的类方法,实际调用的是 People 类中的。
MRO方法
- 方法解析顺序(Method Resolution Order),简称 MRO。对于只支持单继承的编程语言来说,MRO 很简单,就是从当前类开始,逐个搜索它的父类;而对于 Python,它支持多继承,MRO 相对会复杂一些。
发展历史
Python 发展至今,经历了以下 3 种 MRO 算法,分别是:
- 1.从左往右,采用深度优先搜索(DFS)的算法,称为旧式类的 MRO;
- 2.自 Python 2.2 版本开始,新式类在采用深度优先搜索算法的基础上,对其做了优化;
- 3.自 Python 2.3 版本,对新式类采用了 C3 算法。由于 Python 3.x 仅支持新式类,所以该版本只使用 C3 算法。
旧式类MRO算法
### 示例代码
class G:
cls = 'class G'
class F:
cls = 'class F'
class E(G):
pass
class D(G):
cls = 'class D'
class C(F):
pass
class B(E):
pass
class A(B, C, D):
pass
A = A()
print(A.cls)
### 运行结果:
class G
根据深度优先算法,搜索顺序为:A -> B -> E -> G -> C -> F -> D -> G,因此,使用旧式类的 MRO 算法最先搜索得到的是基类 G 中的 cls属性
采用深度优先搜索(DFS)的算法,寻找属性的查找顺序如下图
单调性原则
所谓单调性原则,是指在类存在多继承时,子类不能改变基类的 MRO 搜索顺序,否则会导致程序发生异常。
例如:
class X(object):
pass
class Y(object):
pass
class A(X,Y):
pass
class B(Y,X):
pass
class C(A, B):
pass
通过进行深度遍历,得到搜索顺序为 C->A->X->object->Y->object->B->Y->object->X->object,再进行简化(相同取后者),得到 C->A->B->Y->X->object。
下面来分析这样的搜索顺序是否合理,我们来看下各个类中的 MRO:
- 对于 A,其搜索顺序为 A->X->Y->object;
- 对于 B,其搜索顺序为 B->Y->X->object;
- 对于 C,其搜索顺序为 C->A->B->X->Y->object。
可以看到,B 和 C 中,X、Y 的搜索顺序是相反的,也就是说,当 B 被继承时,它本身的搜索顺序发生了改变,这违反了单调性原则。
新式类MRO算法
为解决旧式类单调性原则,新式类MRO算法基于旧式类功能上进行优化,仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。
### 示例代码
class G:
cls = 'class G'
class F:
cls = 'class F'
class E(G):
pass
class D(G):
cls = 'class D'
class C(F):
pass
class B(E):
pass
class A(B, C, D):
pass
A = A()
print(A.cls)
### 运行结果:顺序为 A -> B -> E -> C -> F -> D -> G (旧式:A -> B -> E -> G -> C -> F -> D -> G)
class G
优化后深度优先搜索(DFS)的算法,寻找属性的查找顺序如下图
C3算法
### 示例代码,用到的是C3算法(广度优先)
class G:
cls = 'class G'
class F:
cls = 'class F'
class E(G):
pass
class D(G):
cls = 'class D'
class C(F):
pass
class B(E):
pass
class A(B, C, D):
pass
A = A()
print(A.cls)
print(A.__mro__) # mro 列表,查看继承顺利列表(只在新式类中有)
### 运行结果:
class F # 在F类中找到了cls属性
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>)
由此可见,C3算法中,寻找属性的查找顺序如下图
最终查找顺序为:A -> B -> E -> C -> F -> D -> G