继承原理、派生重用
一、继承实现原理
关于python到底是如何实现继承,可以通过mro()来理解。首先定义多种继承示例代码:
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 """ from D (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) """
#新式类继承顺序:F->D->B->E->C->A 广度优先
#经典类继承顺序:F->D->B->A->E->C 深度优先
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
1、方法解析顺序(MRO)列表
对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
F.mro()等同于上述示例代码中的F.__mor__
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。这个MRO列表的构造是通过一个C3线性化算法来实现,并遵循以下原则:
1)子类会先于父类被检查
2)多个父类会根据它们在列表中的顺序被检查
3)如果对下一个类存在两个合法的选择,选择第一个父类。
2、继承顺序
在子类继承多个父类时,属性查找方式分深度优先和广度优先两种。
上图中,A为子类分别继承B,C,D类,经典类按照深度优先方法查找。
因此继承顺序是:B->E->G->C->F->D(python2中有经典类和新式类)
上图中,A为子类继承B、C、D三类,新式类按照广度优先方式查找。
新式类继承顺序:B->E->C->F->D->G
(python3中只有新式类)
二、子类重用父类方法及属性
在子类派生的新的方法中重用父类的方法,有两种实现方式:
1、方式一:指名道姓,即父类名.父类方法()
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def attack(self, enemy): # 一旦重新定义了自己的属性且与父类重名,调用新增属性时,就以自己为准。 Hero.attack(self, enemy) # 指名道姓,不依赖于继承(一种重用方式) print('from Garen Class') class Riven(Hero): camp = 'Noxus' g = Garen('草丛伦', 100, 30) r = Riven('锐雯雯', 80, 50) print(r.life_value) g.attack(r) print(r.life_value) """ 80 from Garen Class 50 """
实例化子类添加自己独有特征__init__复用
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # 代码复用 Hero.__init__(self, nickname, life_value, aggresivity) self.weapon = weapon def attack(self, enemy): Hero.attack(self, enemy) # 指名道姓 print('from Garen Class') g = Garen('草丛伦', 100, 30, '金箍棒') print(g.__dict__) """ {'nickname': '草丛伦', 'life_value': 100, 'aggresivity': 30, 'weapon': '金箍棒'} """
2、方式二:super() (依赖继承)
(1)什么是super?
super() 函数是用于调用父类(超类)的一个方法。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
(2)语法
super(type[, object-or-type]) 参数: type -- 类。 object-or-type -- 类,一般是 self
(3)应用示例
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def attack(self, enemy): super(Garen, self).attack(enemy) # 依赖继承 print('from Garen Class') class Riven(Hero): camp = 'Noxus' g = Garen('草丛伦', 100, 30) r = Riven('锐雯雯', 80, 50) g.attack(r) print(r.life_value) """ from Garen Class 50 """
(4)运用super()实例化子类添加独有特征
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # 代码复用 # Hero.__init__(self, nickname, life_value, aggresivity) # super(Garen, self).__init__(nickname, life_value, aggresivity) # python2必须这么写 super().__init__(nickname, life_value, aggresivity) # python3可以这么简写 self.weapon = weapon def attack(self, enemy): Hero.attack(self, enemy) # 指名道姓 print('from Garen Class') g = Garen('草丛伦', 100, 30, '金箍棒') print(g.__dict__) """ {'nickname': '草丛伦', 'life_value': 100, 'aggresivity': 30, 'weapon': '金箍棒'} """
3、指名道姓与super()区别
指名道姓是跟继承没有关系,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找。
super与mro列表的关系
class A: def f1(self): print('from A') super().f1() # super不管A的继承关系,按照C的MRO列表,继续往后找:B class B: def f1(self): print('from B') class C(A,B): pass print(C.mro()) """ [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] """ c = C() c.f1() """ from A from B """
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)