zoukankan      html  css  js  c++  java
  • 面向对象之继承

    class继承

    一:继承介绍

    什么是继承?

    继承就是创建了一个新的类,在Python中,新创建的类可以继承一个或者多个父类(现实社会有点难)新建的类就是子类,围着派生类,父类称之为基类或者超类(累)

    为什么要有继承?

    继承可以减少代码冗余

    如何使用继承?

    class P1:
    	pass
    
    class P2:
        pass
    
    class sub1(P1): # 继承了P1类
        pass
    
    class sub2(P1,P2): # 继承了P1,P2类
        pass
    
    通过类内的__bases__可以查看类继承的所有的父类
    
    print(sub1.__bases__) # (<class '__main__.P1'>,)
    print(sub2.__bases__) # (<class '__main__.P1'>, <class '__main__.P2'>)
    
    

    示列一(继承)

    class School:
        scholl = '东京校区'
        
    class Student(School):
        
        def __init__(self,name,age,gender,suid,course):
            self.name = name
            self.age = age
            self.gender = gender
            self.suid = suid
            self.course = course
        
        def choose(self):
            print(f"{self.name}正在选课")
        
         
    class Teacher(School):
        
        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,student,num):
            student.num = num
            
    s1 = Student('alen',66,'male',10001,'python开发')
    t1 = Teacher('eg',18,'male',2000,10)
    
    print(t1.scholl)    # 东京校区
    print(s1.__dict__)  # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python开发'}
    print(t1.__dict__)  # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}
    

    二:如何在子类中派生新方法重用父类的功能

    方式一:指名道姓地调用某一个类的函数

    特点:可以不以依赖继承关系

    示列

    class School:
        scholl = '东京校区'
        
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        
        
        def t1(self):
            print("这是一个测试继承的函数")
        
    class Student(School):
        
        def __init__(self,name,age,gender,suid,course):
            School.__init__(self,name,age,gender)
            self.suid = suid
            self.course = course
        
        def choose(self):
            print(f"{self.name}正在选课")
        
         
    class Teacher(School):
        
        def __init__(self,name,age,gender,salary,level):
         	School.__init__(self,name,age,gender)
            self.salary = salary
            self.level = level
            
        def score(self,student,num):
            School.t1(self)
            student.num = num
            
    s1 = Student('alen',66,'male',10001,'python开发')
    t1 = Teacher('eg',18,'male',2000,10)
    print(s1.__dict__)   # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python开发'}
    print(t1.__dict__)  # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}
    

    三:属性查找

    在python中如果对象内部没有属性,就去类里去找,如果类里没有就去父类找(如果没有有继承,就找不到报错)

    在python3中会去object里去找,(如果没有默认继承,默认就是继承object类),在python2中,存在两种类(新式类和经典类),两种类的查找循序有点区别

    示列

    class  Foo:
        
        def f2(self):
            print("from foo.f2")
        
        def f1(self):
            print("from foo.f1")
            self.f2()
            
    class Bar(Foo):
        def f2(self):
            print("from Bar.f2")
    
    t = Bar()
    t.f1() 
    """
    from foo.f1
    from Bar.f2
    """
    

    父类如果不想让子类覆盖自己的方法,可以在方法名前加__,让其成为私有属性

    class  Foo:
        
        def __f2(self):
            print("from foo.f2")
        
        def f1(self):
            print("from foo.f1")
            self.__f2()
            
    class Bar(Foo):
        def __f2(self):
            print("from Bar.f2")
    
    obj = Bar()
    obj.f1()
    ​```
    from foo.f1
    from foo.f2
    ​```
    

    四:继承的原理

    基础知识

    新式类:凡是继承了object的子类,以该子类子子孙孙类都称之为新式类

    经典类:没有继承object的子类,以该子类子子孙孙类都称之为经典类

    在python3中全都是新式类,python2中有经典类

    在python3中没有继承任何类,默认基础obj类

    在py3中
    class Foo:
    	pass
    
    print(Foo.__bases__) # (<class 'object'>,)
    在py2中
    class Foo:
    	pass
    print(Foo.__bases__) # 为空
    

    继承的实现原理

    1:菱形问题

    大多数面向对象语言都不支持多继承,在python中,一个子类是可以同时继承多个父类的,这个可以让一个子类,可以对多个不同父类加以重用的好处,但是也有可能引发著名的菱形问题(或称死亡钻石问题)

    如图

    ![菱形继承](D:Program Files (x86)markdownpython3.8菱形继承.png)

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

    这个图展示的就是菱形继承

    如果A中有一个方法,B或者C都重写了该方法,而D没有重写,那么D继承的是哪个版本的方法:是B或者是C?看下面

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

    要想知道obj.test()是如何找到方法test的,需要了解Python的继承实现原理--

    2继承原理

    MRO

    Python是如何实现继承的呢?

    对于你定义的每一个类,python都会计算出方法解析顺(mro)列表,该mro列表就是一个简单的所有基类的线性顺序列表,如下

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

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

    这个mro列表的构造是通过一个C3线性算法来实现的。

    这个算法实际上就是合并所有父类的mro列表并遵循如下原则

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

    属性查找

    1:由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类的mro列表规定的顺序依次找下去
    2:由类发起的属性查找,会按照当前类.mro规定的顺序依次找下去
    

    示列

    #【test1】
    
    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(C,B):
        def test(self):
            print('from D')
    
    print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
    # 输出结果:
    # [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    obj1 = D()
    obj1.test()   # 输出结果:from D
    print(D.test)  # 输出结果:<function D.test at 0x0000012EA27FA4C0>
    
    
    print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
    # 输出结果:[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    obj2 = C()
    obj2.test()  # 输出结果:from C
    print(C.test) # 输出结果: <function C.test at 0x0000022FA7D0A3A0>
    
    # 【test2】
    class A(object):
        # def test(self):
        #     print('from A')
        pass
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        # def test(self):
        #     print('from C')
        pass
    class D(C,B):
        # def test(self):
        #     print('from D')
        pass
    
    print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
    # 输出结果:
    # [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    obj1 = D()
    obj1.test()   # 输出结果:from B
    print(D.test)  # 输出结果:<function B.test at 0x00000246D6F3A310>
    
    # 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro
    
    

    3:深度优先和广度优先

    1:非菱形继承

    如果多继承是非菱形继承,经典类与新式类的属性查找顺序一样:都是一个分支一个分支地找下去,然后找object类

    示列如下

    ![image-20200807201837419](D:Program Files (x86)markdownpython3.8image-20200807201837419.png)

    代码演示

    class E:
        # def test(self):
        #     print('from E')
        pass
    
    class F:
        def test(self):
            print('from F')
    
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    class C(F):
        # def test(self):
        #     print('from C')
        pass
    
    class D:
        def test(self):
            print('from D')
    
    
    class A(B, C, D):
        # def test(self):
        #     print('from A')
        pass
    
    # 新式类
    print(A.mro())  
    # A->B->E->C->F->D->object 
    # [<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 F
    

    2:菱形继承

    如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样

    经典类:深度优先,会在在检索第一天分支的时候就直接一天道走到黑,即会检索(共同的父类)

    新式类:广度优先,会在检索最后一条分支的时候检索共同的父类

    ![image-20200807202406380](D:Program Files (x86)markdownpython3.8image-20200807202406380.png)

    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
    # 如果B没有去找C,c没有然后找A
    python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下
    print(d.mro) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    
    class G:   # 在python2中,未继承object的类及其子类,都是经典类
        # def test(self):
        #     print('from G')
        pass
    
    class E(G):
        # def test(self):
        #     print('from E')
        pass
    
    class F(G):
        def test(self):
            print('from F')
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    class C(F):
        def test(self):
            print('from C')
    
    class D(G):
        def test(self):
            print('from D')
    
    class A(B,C,D):
        # def test(self):
        #     print('from A')
        pass
    
    # # 新式类
    print(A.mro())
    # # A->B->E->C->F->D->G->object
    # # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>,
    # # <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
    obj = A()
    obj.test()  # 输出结果:from C
    
    
    # 经典类:A->B->E->G->C->F->D
    print(A.mro())
    obj = A()
    obj.test()  # 输出结果:from C
    

    3:总结

    多继承到底要不要用?
    python既然提供了,那么就要用,但是要规避问题
    1:继承结构尽量不要过于复杂
    2:推荐使用mixins机制:在多继承的背景下满足继承是什么关系
    

    四:python Mixins机制

    用来解决多继承所带来的问题的,将原本的混乱的继承清晰的变成’is-a‘的关系
    
    # 民航飞机、直升飞机、汽车都是交通工具,但是飞机会飞,汽车不会,
    # 这个时候我们将飞这个功能放到交通工具中就显得不合适
    class Vehicle:  # 交通工具
        def fly(self):
            '''
            飞行功能相应的代码        
            '''
            print("I am flying")
    
    
    class CivilAircraft(Vehicle):  # 民航飞机
        pass
    
    
    class Helicopter(Vehicle):  # 直升飞机
        pass
    
    
    class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
        pass
    
    # 针对上面的问题,就引入Mixin机制
    # Mixin机制指的就是子类混合不同类的功能,然后将其用统一的命名规范来标识该类只是用来混合功能的
    # 从而来遵守多继承下的“is-a”关系,Mixin机制本质仍是多继承
    class Vehicle:  # 交通工具
    	pass
    
    class FlyMixin:
        def fly(self):
            '''
            飞行功能相应的代码        
            '''
            print("I am flying")
    
    
    class CivilAircraft(FlyMixin,Vehicle):  # 民航飞机
        pass
    
    
    class Helicopter(FlyMixin,Vehicle):  # 直升飞机
        pass
    
    
    class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
        pass
    
    '''
    使用Mixin类实现多重继承要非常小心
    1、它必须表示某一种功能,而非物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
    2、它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承
       的“is-a”原则,只能继承一个标识其归属含义的父类
    3、然后,它不依赖于子类的实现
    4、最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。
      (比如飞机照样可以载客,就是不能飞了)
    '''
    

    五:派生与方法重用

    • 子类中衍生出的新东西

      1. 子类独有,父类没有
      2. 子类有,父类也有,子类是完全覆盖父类
      3. 子类有,父类也有,子类在父类的基础上进行扩展
      
      

    class People:
    ... school='清华大学'
    ...
    ... def init(self,name,sex,age):
    ... self.name=name
    ... self.sex=sex
    ... self.age=age
    ...

    class Teacher(People):
    ... def init(self,name,sex,age,title): # 派生
    ... self.name=name
    ... self.sex=sex
    ... self.age=age
    ... self.title=title
    ... def teach(self):
    ... print('%s is teaching' %self.name)

    ```
    
    • 想在子类派生出的方法内重用父类的功能,有两种实现方式

      # 方法一:“指名道姓”地调用某一个类的函数=》不依赖于继承
      >>> class Teacher(People):
      ...     def __init__(self,name,sex,age,title):
      ...         People.__init__(self,name,age,sex) #调用的是函数,因而需要传入self
      ...         self.title=title
      ...     def teach(self):
      ...         print('%s is teaching' %self.name)
      
      # 方法二:super()调用父类提供给自己的方法=》严格依赖继承关系
      # 调用super()会得到一个特殊的对象,该对象会按照发起属性查找的类的mro去当前类的父类中找属性
      >>> class Teacher(People):
      ...     def __init__(self,name,sex,age,title):
      ...         super().__init__(name,age,sex) #调用的是绑定方法,自动传入self
      ...         self.title=title
      ...     def teach(self):
      ...         print('%s is teaching' %self.name)
      
      
      >>> #A没有继承B
      ... class A:
      ...     def test(self):
      ...         super().test()
      ... 
      >>> class B:
      ...     def test(self):
      ...         print('from B')
      ... 
      >>> class C(A,B):
      ...     pass
      ... 
      >>> C.mro() # 在代码层面A并不是B的子类,但从MRO列表来看,属性查找时,就是按照顺序C->A->B->object,B就相当于A的“父类”
      [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>]
      >>> obj=C()
      >>> obj.test() # 属性查找的发起者是类C的对象obj,所以中途发生的属性查找都是参照C.mro()
      from B
      

      六:组合

      什么是组合?

      • 一个类中以另外一个类的对象作为数据属性,作为类的组合
      # 组合与继承都是用来解决代码的重用性问题。
      # 二者的区别在于
      '''
      继承:强调‘是’的关系,例如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承
      组合:强调‘有’的关系,例如老师有多门课程,老师有给学生打分,当类之间有显著不同,并且较小的类是较大的类所需
      要的组件时,应该使用组合
      '''
      class Course:
          def __init__(self,name,period,price):
              self.name=name
              self.period=period
              self.price=price
          def tell_info(self):
              print('<%s %s %s>' %(self.name,self.period,self.price))
      
      class Date:
          def __init__(self,year,mon,day):
              self.year=year
              self.mon=mon
              self.day=day
          def tell_birth(self):
             print('<%s-%s-%s>' %(self.year,self.mon,self.day))
      
      class People:
          school='清华大学'
          def __init__(self,name,sex,age):
              self.name=name
              self.sex=sex
              self.age=age
      
      #Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
      class Teacher(People): #老师是人
          def __init__(self,name,sex,age,title,year,mon,day):
              super().__init__(name,age,sex)
              self.birth=Date(year,mon,day) #老师有生日
              self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
          def teach(self):
              print('%s is teaching' %self.name)
      
      
      python=Course('python','3mons',3000.0)
      linux=Course('linux','5mons',5000.0)
      teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)
      
      # teacher1有两门课程
      teacher1.courses.append(python)
      teacher1.courses.append(linux)
      
      # 重用Date类的功能
      teacher1.birth.tell_birth()
      
      # 重用Course类的功能
      for obj in teacher1.courses: 
          obj.tell_info()
      

    参考

    https://zhuanlan.zhihu.com/p/109331525

    https://www.cnblogs.com/linhaifeng/articles/7340153.html

  • 相关阅读:
    心血来潮学python
    lua中的table、stack和registery
    wrapper for lua
    FT232RL变砖之后
    12.04 redmine
    Webpack 概念
    如何学习 Webpack
    React基础篇
    Intellij实用技巧
    一行代码搞定图片缩放、旋转、加水印
  • 原文地址:https://www.cnblogs.com/wait59/p/13458488.html
Copyright © 2011-2022 走看看