zoukankan      html  css  js  c++  java
  • 🍔类的菱形继承问题

    一.类的分类

    在前面一章我们简单介绍了一些类的分类

    1.新式类

    • 继承了 object 的类以及该类的子类, 都是新式类 (Python3中统一都是新式类)
    • Python3 中如果一个类没有继承任何类, 则默认会继承 object 类, 也就是Python3中所有的类都是新式类
    在"Python3"中
    class Default:  # 默认继承"object"
        pass
    
    print(Default.__bases__)  # (<class 'object'>,)
    

    2.经典类

    • 没有继承 object 的类以及该类的子类, 都是经典类 (只有Python2中才区分新式类和经典类)
    • Python2 中如果一个类没有继承任何类, 它不会继承object 类, 所以Python2 中才有经典类
    在"Python2"中
    class Default(object):  # 新式类
        pass
    
    class Animal:  # 经典类
        pass
    
    "Python2"中"print"语法
    print Default.__bases__  # (<class 'object'>,)
    print Animal.__bases__   # ()
    

    ps : 新式类与经典类的属性查找顺序是不一样的


    二.菱形继承问题

    上面说到Python支持多继承, 但新式类与经典类的属性查找顺序是不一样的

    java语言中,它不支持多继承,只支持继承一个父类

    python语言,支持多继承,比如 A(B,C,D)

    1.非菱形结构

    • 非菱形结构下, 经典类与新式类的属性查找顺序是一样的

    • 如果继承关系为非菱形结构,则会按照先找 B 这一条分支,然后再找 C 这一条分支,最后找 D 这一条分支的顺序直到找到我们想要的属性

    • 非菱形结构 (两条查询路径最后分别继承了 F1 和 F4)

    class F1:
        def s1(self):
            print('F1:s1')
        def s2(self):
            print('F1:s2')
    
    class F2(F1):
        # def s1(self):
        #     print('F2:s1')
        def s2(self):
            print('F2:s2')
    
    class F4():
        def s1(self):
            print('F4:s1')
        def s2(self):
            print('F4:s2')
    
    class F3(F2,F4):
        def s1(self):
            super().s1()  # 调用父类的s1方法,到底使用了哪个父类的s1
        def s2(self):
            print('F3:s2')
    
    
    f1 = F3()
    f1.s1()  # F1:s1
    
    • 查找顺序 : F3 ===> F2 ===> F1

    2.菱形结构

    • 如果继承关系为菱形结构,即子类的父类最后继承了同一个类,那么属性的查找方式有两种:
      • 新式类----->广度优先
      • 经典类----->深度优先

    1、新式类----->广度优先

    • 不找多各类最后继承的同一个类,直接去找下一个父类
    • __mro__ : 只有新式类才有的属性, 可查看属性查找的顺序

    img

    class G(object):
        # def test(self):
        #     print('from G')
        pass
    
    class E(G):
        # def test(self):
        #     print('from E')
        pass
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    class F(G):
        # def test(self):
        #     print('from F')
        pass
    
    class C(F):
        # def test(self):
        #     print('from C')
        pass
    
    class D(G):
        # def test(self):
        #     print('from D')
        pass
    
    class A(B, C, D):
        def test(self):
            print('from A')
    
    obj = A()
    obj.test()
    print(A.__mro__)
    '''查找顺序
    (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <type 'object'>)
    '''
    

    2、经典类----->深度优先

    • 从左到右选路, 一条路走到黑

    img

    #coding:utf-8
    # python2 解释器
    
    class G():
        pass
        def test(self):
            print('G---test')
    
    class F(G):
        pass
        # def test(self):
        #     print('FFF---test')
    class E(G):
        pass
        # def test(self):
        #     print('EEE---test')
    class D(G):
        pass
        # def test(self):
        #     print('DDD---test')
    class B(E):
        pass
        # def test(self):
        #     print('BBB---test')
    class C(F):
        pass
        # def test(self):
        #     print('CCC---test')
    
    class A(B,C,D):
        pass
        # def test(self):
        #     print('AAA---test')
    
    a=A()
    a.test()  # G---test
    

    三.小示例

    1.新式类

    • 菱形结构示例 (子类最后继承了同样的类, 都继承了 A1 和 A2), 可以看成两个菱形

    4d1651e9189743b96c356a9fc9054ef

    class A1:
        def foo(self):
            print('A1_foo')
    
    class A2:
        def foo(self):
            print("A2_foo")
    
        def bar(self):
            print("A2_bar")
    
    class B1(A1, A2):
        pass
    
    class B2(A1, A2):
        def bar(self):
            print("B2_bar")
    
    class C(B1, B2):
        pass
    
    c = C()
    c.foo()  # A1_foo
    c.bar()  # B2_bar
    
    print(C.__mro__)
    '''输出
    (<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A1'>, <class '__main__.A2'>, <class 'object'>)
    '''
    
    • c.foo() 的查找顺序 : C ==> B1 ===> B2 ==> A1
    • c.bar() 的查找顺序 : C ==> B1 ==> B2

    2.经典类

    d415228a749e9175eaa2aea308686a7

    #coding:utf-8
    # python2.7 解释器
    class A1():
        def foo(self):
            print 'A1_foo'
    
    class A2():
        def foo(self):
            print "A2_foo"
    
        def bar(self):
            print "A2_bar"
    
    class B1(A1, A2):
        pass
    
    class B2(A1, A2):
        def bar(self):
            print "B2_bar"
    
    class C(B1, B2):
        pass
    
    c = C()
    c.foo()  # A1_foo
    c.bar()  # A2_bar
    
    • c.foo() 的查找顺序 : C ==> B1 ===> A1
    • c.bar() 的查找顺序 : C ==> B1 ==> A1 ===> A2

    四. c3 算法与 mro

    1.简介

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

    而这个MRO列表的构造是通过一个C3线性化算法来实现的

    我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则 :

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

    2.__mro__ 方法使用

    其实上面我们已经使用了这种方法, 下面再补充一点

    • 只有新式类才可以使用 (Python2中的类如果没继承object就无法使用)
    • [类名]__mro__ : 打印属性查找顺序, 是一个元组
    • [类名].mro() : 打印属性查找顺序, 是一个列表
    Python2 中
    #coding:utf-8
    
    class C(object):  # 继承object类,如果不继承,调用 mro 方法会报错 
        def run(self):
            print "run_C"
    
    class B(C):
        pass
    
    class A(B):
        pass
    
    a = A()
    a.run()  # run_C
    print A.__mro__  
    # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>)
    
    print A.mro()  
    # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>]
    
    Python3 中
    class C:  # 已经自动继承了object类
        def run(self):
            print("run_C")
    
    class B(C):
        pass
    
    class A(B):
        pass
    
    a = A()
    a.run()  # run_C
    print(A.__mro__)
    # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
    
    print(A.mro())
    # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
    
  • 相关阅读:
    无法删除文件提示找不到指定文件导致文件无法删除的解决方法
    c++多线程编程(三)
    c++多线程编程(二)
    c++多线程编程(一)
    面试中的C++常见问题
    展示组件(Presentational component)和容器组件(Container component)之间有何不同
    如果你创建了类似于下面的 Twitter 元素,那么它相关的类定义是啥样子的?
    React 中 refs 的作用是什么?
    typescript 类(类的定义、继承、修饰符、抽象类)
    typescript 接口 interface
  • 原文地址:https://www.cnblogs.com/songhaixing/p/14186316.html
Copyright © 2011-2022 走看看