当前位置 : 主页 > 手机开发 > 其它 >

第七章 面向对象(4):继承的实现原理与派生的扩展知识

来源:互联网 收集:自由互联 发布时间:2021-06-19
7.6 继承的实现原理 在单继承的情况下: 类的属性查找顺序是:对象类父类...最上级父类报错 单如果是多继承的情况呢? 想了解这个,需要知道python继承的原理。 本节我们来了解下

7.6 继承的实现原理

  • 在单继承的情况下:
    • 类的属性查找顺序是:对象>类>父类...>最上级父类>报错
  • 单如果是多继承的情况呢? 想了解这个,需要知道python继承的原理。

本节我们来了解下python继承的原理

在python中,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)的列表
这个MRO列表就是一个简单的所有鸡肋的线性顺序列表。

可以用方法F.mro()或者F.__mro__来输出此列表(从左到右开始查找父类,知道第一个匹配这个属性的类为止)

MRO列表的构造是通过一个C3线性化算法来实现的。数学原理我们不深究,但实际上就是 合并所有父类的MRO列表并遵循如下3条准则:

  1. 子类会先于父类被查找
  2. 多个父类会根据他们在列表中的顺序被查找
  3. 如果对下一个类存在两个合法的选择,选择第一个父类

在JAVA和C#中子类只能继承一个父类,而python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种: 1.深度优先(经典类); 2.广度优先(新式类) ※其实本质就是MRO列表的排列顺序不同

  • python2中的类分为两种,python3中只有新式类
    • 新式类:继承object的类,以及他的子类都称之为新式类。※新式类有F.mro()或者F.__mro__的方法来查看查找顺序
    • 经典类:没有继承object的类,以及他的子类
  • python3中,只有新式类:
    • 新式类:即使不写明继承object类,也会默认继承object类

新式类-python2中:

class Foo(object):
    pass

class Bar(Foo):
    pass

经典类python2中:

class Foo:
    pass

class Bar(Foo):
    pass

新式类-python3中:

class Foo:  # 默认继承object 
    pass

class Bar(Foo):
    pass
    
print(Foo.__bases__)
print(Bar.mro())

python3 执行结果:

(<class 'object'>,)
[<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>]

那什么是深度优先,什么是广度优先呢?

  • 深度优先(经典类):先一条路查到最上层父类,然后查其他的父类。

  • 广度优先(新式类):先一条路查到最上层父类的第一层子类为止,然后查其他路径,所有子类查完,最后查最上层父类

因为是python3环境,所以我们举例说明下广度优先:

class A:
    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):  # 继承类时候写的顺序会影响查找到的顺序!!!! --> 先D的路线查找, 后E的路线查找
    pass

class G(E,D):  # 继承类时候写的顺序会影响查找到的顺序!!!! --> 先E的路线找, 后D的路线找
    pass

g = G()
f = F()

f.test()
print(F.mro())  # 新式类可以使用此方法: F.mro() or F.__mro__
g.test()
print(G.mro())

执行结果,python3中是广度优先,所以最后才会查找A类:

from D
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
from E
[<class '__main__.G'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

7.7 派生的扩展:在子类中重用父类属性

子类派生的方法的时候会覆盖父类中的方法,这样我们想用父类方法的时候怎么办呢?

  • 在子类派生的新方法中重用父类的方法:
    • 方式1,指名道姓:在派生方法中直接调用父类的方法。这种方法不依赖于继承,也可以写不相关类的方法
    • 方式2:super()。这种方式依赖于继承

      super()在MRO列表的下一个类中查找

class Hero:
    def __init__(self, nickname, hp, atk):
        self.Nickname = nickname
        self.Hp = hp
        self.Atk = atk
    def attack(self, enemy):
        enemy.Hp -= self.Atk

class Gairun(Hero):
    camp = 'Demacia'
    
    def __init__(self, nickname, hp, atk, weapon):
        # self.Nickname = nickname
        # self.Hp = hp
        # self.Atk = atk
        Hero.__init__(self, nickname, hp, atk)  # 调用父类的方法
        self.Weapon = weapon
        
    def attack(self, enemy):  # 派生方法
        Hero.attack(self, enemy)  # 指名道姓
        print('from Geren Class')
    

class Reven(Hero):
    camp = 'Noxus'
    
    def __init__(self, nickname, hp, atk, weapon):
        super().__init__(nickname, hp, atk)  # super()方式,调用父类的对象。python3 可以直接省略super()的参数
        self.Weapon = weapon
        
    def attack(self, enemy):
        super(Reven, self).attack(enemy)  # python2中,super()需要写明参数,(方法所在类名,self)
        print('from Reven Class')

h1 = Gairun('a', 100, 10, 'sword')
h2 = Reven('b', 70, 30, 'knife')

print(h1.__dict__)
print(h2.__dict__)

h1.attack(h2)
h2.attack(h1)

print(h1.Hp)
print(h2.Hp)

super()根据实例化对象的类的MRO列表查找

注意:super()找的并不是父类,而是MRO列表的下一个类:

class A:
    def f1(self):
        print('A')
        super().f1()

class B:
    def f1(self):
        print('B')
        
class C(A,B):
    pass

print(C.mro())  # 结果:[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

c = C()  # 实例化
c.f1()  
# 由于继承,class C中没有f1(),进而到class A中找,
# class A的f1()中调用了super().f1(),虽然B不是A的父类,
# 但super是根据MRO列表查找的,所以super会寻找MRO列表的下一个类(class B)并执行

执行结果:

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
A
B
网友评论