• 面向对象之继承与派生


    一、继承

      1、含义:继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。

      2、特点:继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,而父类又可以称为基类或超类,新建的类称为派生类或子类。

      3、继承的分类

        Python中类的继承可分为单继承和多继承。

    # 单继承
    class Hero:
        '''
        父类
        '''
        def __init__(self,nickname,aggressivity,life_valid):
            self.nickname = nickname
            self.aggressivity = aggressivity
            self.life_valid = life_valid
        def attack(self,target):
            target.life_valid -= self.aggressivity
            if target.life_valid <= 0:
                print('%s is died'%target.nickname)
    
    class Gaylen(Hero): #单继承,基类是Hero,派生类是Gaylen
        '''
        继承父类
        '''
        pass
    class Hand_of_Knoxus(Hero):#单继承,基类是Hero,派生类是Hand_of_Knoxus
    ''' 继承父类 ''' 
      pass
    herol = Gaylen('大盖伦',60,300)
    hero2
    = Hand_of_Knoxus('诺手',70,280)
    print(herol.__dict__) # {'nickname': '大盖伦', 'aggressivity': 60, 'life_valid': 300}
    herol.attack(hero2)
    print(hero2.life_valid) # 220

    #多继承
    class Parent1: #定义父类一
      pass
    class Parent2: #定义父类二
      pass
    class son1(Parent1,Parent2): #Python支持多继承,用逗号分隔多个继承的类
      pass

    #查看继承
    print(
    son1.__base__)# __base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    #(<class '__main__.Parent1'>)

    print(son1.__bases__) #(<class '__main__.Parent1'>,<class '__main__.Parent2'>)
    
    

      4、单继承中对象属性的查找顺序

        <1>对象自身属性(__init__方法)里查找

        <2>对象自身里没有,在自己的类里找

        <3>自己的类里没有,在父类里查找 

    # 属性查找
    class Father:
        def f1(self):
            print('from Father.f1')
        def f2(self):
            print('from Father.f2')
            self.f1() # 等价于person.f1(),查找:person自身->自身的类(Son)
    class Son(Father):
        def f1(self):
            print('from Son.f1')
    
    person = Son()
    print(person.__dict__) # {} 自身没有定义属性
    person.f2() # 单继承查找顺序:person自身—>自身的类(Son)->父类(Father)

     二、派生

      1、含义:

         派生:子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),对象属性查找中按照自身,后类,再父类的顺序,当某些属性自身类中有而父类无或者两者都有时,优先选择自身类中的属性。

      2、实例:

    class Hero:
        '''
        父类
        '''
        def __init__(self,nickname,aggressivity,life_valid):
            self.nickname = nickname
            self.aggressivity = aggressivity
            self.life_valid = life_valid
        def attack(self,target):
            target.life_valid -= self.aggressivity
            if target.life_valid <= 0:
                print('%s is died'%target.nickname,'
    from Hero.attack')
    
    class Gaylen(Hero):
        '''
        继承父类
        '''
        camp = 'Desmarcia'
        def attack(self,target):
            target.life_valid -= self.aggressivity
            if target.life_valid <= 0:
                print('%s is died'%target.nickname,'
    from Gaylen.attack')
    class Hand_of_Knoxus(Hero):
        '''
        继承父类
        '''
        camp = 'Desmarcia'

    herol = Gaylen('大盖伦',80,190) hero2 = Hand_of_Knoxus('诺手',100,150) herol.attack(hero2) herol.attack(hero2) ''' 输出: 诺手 is died from Gaylen.attack '''

     

    三、继承的实现原理

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

      2、为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。简单来说就是合并所有父类的MRO列表并遵循下述原则:

      <1>子类会优先于父类被检查

      <2>多个子类会根据它们在列表中的顺序被检查。

      <3>如果对下一个类存在两个合法的选择,选择第一个父类

      注意:

        在Java和C#只能继承一个父类,而Python中子类可以继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先。

        通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的

    成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为父类,也就是基类。

    四、多继承下的属性查找(广度优先)

    class A(object):
        def test(self):
            print('from A')
    class B(A):
        # def test(self):
        #     print('from B')
        pass
    class C(A):
        # def test(self):
        #     print('from C')
        pass
    class D(B):
        # def test(self):
        #     print('from D')
        pass
    class E(C):
        # def test(self):
        #     print('from E')
        pass
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    
    f = F()
    f.test() # 查找顺序:f->F类->D类->B类->E类->C类->A类
    print(F.mro()) # 新式类中才有这个属性可以查看线性列表,经典类没有
    # [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>,
    # <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    View Code

     

    五、经典类和新式类

      1、在Python2中-》经典类:没有继承object的类,以及他的子类都称之为经典类。 深度优先查找

        如下:

    class Father:
        pass
    class Son(Father):
        pass

      2、在Python2中-》新式类:有继承object的类,以及他的子类都称之为新式类。 广度优先查找

        如下:

    class Father(object):
        pass
    class Son(Father):
        pass

      3、在Python3中-》新式类(无经典类之分):一个类没有继承object类,就默认继承object。 广度优先查找

    class Father:
         pass
     print(Father.__bases__) # (<class 'object'>,) 在Python中,每个类有一个__bases__属性,列出其基类

      补充:

     六、在子类中重用父类的属性

      1、在子类派生的新的方法中重用父类的方法,有两种实现方式:

        方式一:指明道姓(不依赖继承)

        方式二:super() (依赖继承)-->遵照MRO列表顺序

      2、方式一:   

    class Hero:
        
        # 父类
        
        def __init__(self,nickname,aggressivity,life_valid):
            self.nickname = nickname
            self.aggressivity = aggressivity
            self.life_valid = life_valid
        def attack(self,target):
            target.life_valid -= self.aggressivity
            if target.life_valid <= 0:
                print('%s is died'%target.nickname,'
    from Hero.attack')
    
    class Gaylen(Hero):
        
        #继承父类
        
        camp = 'Desmarcia'
        def __init__(self,nickname,aggressivity,life_valid,arms):
            Hero.__init__(self,nickname,aggressivity,life_valid) # 指名道姓地重用方式
            self.arms = arms
        def attack(self,target):
            Hero.attack(self,target) # 指名道姓地重用方式,不依赖继承
            print('from Gaylenlen ...')
    
    class Hand_of_Knoxus(Hero):
    
        #继承父类
    
        camp = 'Desmarcia'
    herol = Gaylen('大盖伦',80,190,'大刀')
    hero2 = Hand_of_Knoxus('诺手',100,150)
    herol.attack(hero2)
    print(hero2.life_valid) # 70
    print(herol.arms) # 大刀
    View Code

      3、方式二: 

    class Hero:
        
        # 父类
        
        def __init__(self,nickname,aggressivity,life_valid):
            self.nickname = nickname
            self.aggressivity = aggressivity
            self.life_valid = life_valid
        def attack(self,target):
            target.life_valid -= self.aggressivity
            if target.life_valid <= 0:
                print('%s is died'%target.nickname,'
    from Hero.attack')
    
    class Gaylen(Hero):
        
        #继承父类
        
        camp = 'Desmarcia'
        def __init__(self,nickname,aggressivity,life_valid,arms):
            Hero.__init__(self,nickname,aggressivity,life_valid) # 指名道姓地重用方式
            self.arms = arms
        def attack(self,target):
            Hero.attack(self,target) # 指名道姓地重用方式,不依赖继承
            print('from Gaylenlen ...')
    
    class Hand_of_Knoxus(Hero):
    
        #继承父类
    
        camp = 'Desmarcia'
    herol = Gaylen('大盖伦',80,190,'大刀')
    hero2 = Hand_of_Knoxus('诺手',100,150)
    herol.attack(hero2)
    print(hero2.life_valid) # 70
    print(herol.arms) # 大刀
    View Code

      4、super()继承查找顺序  

    class A:
        def f1(self):
            print('from A')
            super().f1() # 从最近查找的MRO节点继续往后找(此时按照的是C的MRO列表),故B虽不是A的父类,但仍会继承B
    class B:
        def f1(self):
            print('from B')
    class C(A,B):
        pass
    c = C()
    c.f1() # from A -->from B
    print(C.mro())
    # 查找顺序
    # [<class '__main__.C'>,
    # <class '__main__.A'>,
    # <class '__main__.B'>,
    # <class 'object'>]
  • 相关阅读:
    html表单提交的几种方法
    ORACLE-SQLLOAD导入外部数据详解
    js 技巧1
    js 细节
    问题链接
    abstract 抽象类
    修饰符 public、 private 和 protected和区别
    HTML5新特性之Mutation Observer
    img 标签上的src 链接图片不存在时 怎么处理
    npm 用 淘宝代理
  • 原文地址:https://www.cnblogs.com/schut/p/8612572.html
Copyright © 2020-2023  润新知