zoukankan      html  css  js  c++  java
  • 菱形问题、类的组合


    菱形继承问题

    在Java中和C#中子类只能继承一个父类,而Python中子类可以继承多个父类,这就会出现多个父类中有相同属性时的属性查找顺序问题。

    菱形指的是类的继承最后会形成一个闭环,一个子类的多个父类最后都会再继承同一个父类,那么它们的继承关系就是菱形结构或钻石结构。

    如果继承关系为菱形结构,那么属性查找的方式有两种:深度优先和广度优先。


    深度优先

    深度优先会顺着一个父类继承关系一层一层找,直到最后一层,没有找到则去第二个父类,当都没有找到属性时,就会抛出异常。

    当类是经典类是,多继承情况下,在要查找属性不存在时,会按照深度优先的方式查找下去。

    img

    # 在Python2中执行.
    
    class G:
        def f1(self):
            print('G.f1')
    
    class F(G):
        def f1(self):
            print('F.f1')
    
    class E(G):
        def f1(self):
            print('E.f1')
    
    class D(G):
        def f1(self):
            print('D.f1')
    
    class C(F):
        def f1(self):
            print("C.f1")
    
    class B(E):
        def f1(self):
            print('B.f1')
    
    class A(B,C,D):
        pass
    
    a = A()
    a.f1()
    # 依次注释f1方法即可验证
    

    广度优先

    不找多个类最后继承的同一个类,直接去找下一个父类,直到最后一个直接父类才会去找最后继承的同一个父类。

    当类是新式类时,多继承情况下,在要查找属性不存在时,会按照广度优先的方式查找下去。

    92-菱形继承问题-新式类.png

    # 在Python3中执行
    class G(object):
        def f1(self):
            print('G.f1')
    
    class F(G):
        def f1(self):
            print('F.f1')
    
    class E(G):
        def f1(self):
            print('E.f1')
    
    class D(G):
        def f1(self):
            print('D.f1')
    
    class C(F):
        def f1(self):
            print("C.f1")
    
    class B(E):
        def f1(self):
            print('B.f1')
    
    class A(B,C,D):
        pass
    
    
    a = A()
    a.f1()
    

    只有当继承为菱形结构时,才会有这个广度优先和深度优先的区别,如果非菱形结构,则是按每个父类逐层查找属性直到最后一层,然后再去下一个父类依次查找。

    Python3中判断是否是菱形结构并不包括object类。


    C3算法与mro()方法

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

    print(A.mro())
    
    [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
    

    为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

    class A:
        def test(self):
            print('from A')  # 先执行这条,打印出from A
            super().test1()  # super会按照MRO列表查找,会直接查找基类,A里面没有则去B,所以打印from B
    
    class B:
        def test1(self):
            print('from B')
    
    class C(A,B):
        def test1(self):
            print('from C')
    
    obj = C()
    obj.test()
    print(C.mro())
    

    结果为:

    from A
    from B
    [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
    

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

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

    类的组合

    在一个类中以另外一个类的对象作为数据属性,称为类的组合。

    class Equip:
        def fire(self):
            print('release Fire skill')
            
    class River:
        camp = 'Noxus'
        def __init__(self,nickname):
            self.nickname = nickname
            self.equip = Equip()   # 用Equip类产生一个装备,赋值给实例的equip属性。
            
    r1 = RIven('瑞文')
    r1.equip.fire()   # 可以使用组合的类产生的对象所持有的方式。
    
    release Fire skill
    

    继承与组合都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同:

    1. 继承的方式

      通过继承建立了派生类与基类之间的关系,它是一种 “ 是 ” 的关系,比如黑猩猩是猩猩,程序猿是人。

      当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。

    2. 组合的方式

      用组合的方式建立了类与组合的类之间的关系,它是一种 “ 有 ” 的关系,比如,程序猿有生日,程序猿有开发技能。

      当类之间有显著的不同,并且较小的类是较大的类所需要的组件时,用组合比较好。


  • 相关阅读:
    ubuntu 14.4 apache2 django
    github上的版本和本地版本冲突的解决方法
    Javascript能做什么 不能做什么。
    django 取model字段的verbose_name值
    Django在admin模块中显示auto_now_add=True或auto_now=True的时间类型列
    合并多个python list以及合并多个 django QuerySet 的方法
    摘抄
    Python 字符串拼接
    学习HTTP
    Django 自定义模板标签和过滤器
  • 原文地址:https://www.cnblogs.com/ChiRou/p/14206615.html
Copyright © 2011-2022 走看看