• 类的继承属性,与其多属性继承问题。


    今日内容

    继承

    什么是继承

    继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为父类,基类,超类

    要注意:python是支持多继承的。

    为什么要继承

    子类会遗传父类的属性,所以继承是用来解决类与类之间代码冗余问题

    如何实现继承

    class Parent1:
        pass
    class Parent2:
        pass
    class Sub1(Parent1):
        pass
    class Sub2(Parent1,Parent2):
        pass
    print(Sub1.__bases__)
    print(Sub2.__bases__)
    

    查看父类信息:

    (<class '__main__.Parent1'>,)
    (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
    

    继承案例1:变量的继承

    子类会继承父类中的东西,例如school属性

    class OldboyPeople:
        school = "oldboy"
    class Student(OldboyPeople):
        def __init__(self,name,age,gender,stud_id,course):
            self.name = name
            self.age = age
            self.gender = gender
            self.stu_id = stud_id
            self.course = course
        def choose(self):
            print('%s 正在选课' %self.name)
            
    class Teacher(OldboyPeople):
        def __init__(self,name,age,gender,salary,level):
            self.name = name
            self.age = age
            self.gender = gender
            self.salary = salary
            self.level = level
        def score(self,stu,num):
            stu.num = num
    stu1=Student("艾利克斯",73,'male',1001,"python全栈开放")
    tea1=Teacher("egon",18,'male',2000,10)
    print(stu1.school)
    print(tea1.school)
    

    结果:

    oldboy
    oldboy
    

    那么我们能把我们子类中一些相同的东西给放到父类中去调用吗

    例如我们都重复的一个步骤

    self.name = name
    self.age = age
    self.gender = gender
    

    继承案例2:函数的继承

    class OldboyPeople:
        school = "oldboy"
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
            
    class Student(OldboyPeople):
        def __init__(self,name,age,gender,stud_id,course):
            OldboyPeople.__init__( self,name,age,gender)
            self.stu_id = stud_id
            self.course = course
        def choose(self):
            print('%s 正在选课' %self.name)
    
    class Teacher(OldboyPeople):
        def __init__(self,name,age,gender,salary,level):
            OldboyPeople.__init__( self,name,age,gender)
            self.salary = salary
            self.level = level
        def score(self,stu,num):
            stu.num = num
    
    stu1=Student("艾利克斯",73,'male',1001,"python全栈开放")
    tea1=Teacher("egon",18,'male',2000,10)
    
    print(stu1.school)
    print(stu1.name)
    print(stu1.age)
    print(stu1.gender)
    print(tea1.name)
    print(tea1.age)
    print(tea1.gender)
    

    结果:我们能否实现呢?

    oldboy
    艾利克斯
    73
    male
    egon
    18
    male
    

    很明显我们实现了,但是这只是继承中的一个方法,我们完全就是相当于指定对象去调用哪个函数了。就算不是父子类,我们也可以这样去调用那个父类的函数。

    继承与抽象

    要找出类与类之间的继承关系,需要先抽象,再继承。抽象即总结相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类,如下图所示

    在这里插入图片描述

    基于抽象的结果,我们就找到了继承关系

    在这里插入图片描述

    属性查找

    父类子类属性查找关系:

    示例1:父类中有,子类中也有的情况

    class Foo:
        def f2(self):
            print("Foo.f2")
        def m1(self):
            print('Foo.m1')
        def f1(self):
            print('Foo.f1')
            self.m2()
            self.m1()
            self.f2()  # obj.f2()
    
    class Bar(Foo):
        def f2(self):
            print("Bar.f2")
        def m2(self):
            print('Bar.m2')
    
    obj=Bar()
    obj.f1()
    

    结果:

    Foo.f1
    Bar.m2
    Foo.m1
    Bar.f2
    

    从结果我们可以看出来,查询的顺序是由,对象—》对象的类–》父类…

    所以我们如果在对象的类中查到了需要的属性或者数据,就不会去父类查看,哪怕父类中也有,也不回去取。

    例2:父类如果不想让子类覆盖自己的方法,可以在方法名前加前缀_

    class Foo:
        def __f2(self):  # _Foo__f2
            print("Foo.f2")
    
        def m1(self):
            print('Foo.m1')
    
        def f1(self):
            print('Foo.f1')
            self.m2()
            self.m1()
            self.__f2()  # obj.f2()
            
    class Bar(Foo):
        def __f2(self):  # _Bar__f2
            print("Bar.f2")
    
        def m2(self):
            print('Bar.m2')
    
    obj = Bar()
    obj.f1()
    

    结果:

    Foo.f1
    Bar.m2
    Foo.m1
    Foo.f2
    

    有上述的结果我们可以看出,–可以将属性隐藏,成为自己独有的,不会被子类覆盖,

    是应为类在一开始运行的时候就已经将,–属性的变量改变成了它独有的,可以在它内部调用,但是在外部想去调用它,需要-类名–属性名以这样的格式去查看。所以从代码的注释可以看出,他们运行以后的格式。

    继承的实现原理

    • 1.菱形问题

    • 大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻

    在这里插入图片描述

    A类在顶部,B类和C类分别位于其下方,D类在底部将两者连接在一起形成菱形。
    

    这种继承结构下导致的问题称之为菱形问题:如果A中有一个方法,B和/或C都重写了该方法,而D没有重写它,那么D继承的是哪个版本的方法:B的还是C的?如下所示

    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,C):
        pass
    
    obj = D()
    obj.test() # 结果为:from B
    

    要想搞明白obj.test()是如何找到方法test的,需要了解python的继承实现原理

    • 2.继承原理

    • D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
      [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
      

    python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

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

    所以obj.test()的查找顺序是,先从对象obj本身的属性里找方法test,没有找到,则参照属性查找的发起者(即obj)所处类D的MRO列表来依次检索,首先在类D中未找到,然后再B中找到方法test

    1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
    2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,
    
    • 深度优先和广度优先

    • 参照下述代码,多继承结构为非菱形结构,此时,会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

    • class E:
          def test(self):
              print('from E')
      
      
      class F:
          def test(self):
              print('from F')
      
      
      class B(E):
          def test(self):
              print('from B')
      
      
      class C(F):
          def test(self):
              print('from C')
      
      
      class D:
          def test(self):
              print('from D')
      
      
      class A(B, C, D):
          # def test(self):
          #     print('from A')
          pass
      
      
      print(A.mro())
      '''
      [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
      '''
      
      obj = A()
      obj.test() # 结果为:from B
      # 可依次注释上述类中的方法test来进行验证
      

    在这里插入图片描述

    • 需要注意:

    • 如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

    • python Mixins机制

    • Python语言可没有接口功能,但Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下

    • class Vehicle:  # 交通工具
          pass
      
      class FlyableMixin:
          def fly(self):
              '''
              飞行功能相应的代码        
              '''
              print("I am flying")
      
      class CivilAircraft(FlyableMixin, Vehicle):  # 民航飞机
          pass
      
      class Helicopter(FlyableMixin, Vehicle):  # 直升飞机
          pass
      
      class Car(Vehicle):  # 汽车
          pass
      

    需要注意:

    使用Mixin类实现多重继承要非常小心

    • 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
    • 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
    • 然后,它不依赖于子类的实现
    • 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
    努力学习!
  • 相关阅读:
    yzoj P2344 斯卡布罗集市 题解
    yzoj P2350 逃离洞穴 题解
    yzoj P2349 取数 题解
    JXOI 2017 颜色 题解
    NOIP 2009 最优贸易 题解
    CH 4302 Interval GCD 题解
    CH4301 Can you answer on these queries III 题解
    Luogu2533[AHOI2012]信号塔
    Luogu3320[SDOI2015]寻宝游戏
    Luogu3187[HNOI2007]最小矩形覆盖
  • 原文地址:https://www.cnblogs.com/Orange-YXH/p/13648106.html
Copyright © 2020-2023  润新知