继承原理、派生重用

一、继承实现原理

  关于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列表

 

 
posted @ 2018-04-23 15:09  休耕  阅读(403)  评论(0编辑  收藏  举报