面向对象之继承与派生

Posted 暮光微凉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象之继承与派生相关的知识,希望对你有一定的参考价值。

一、继承

  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,\'\\nfrom 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,\'\\nfrom 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,\'\\nfrom 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,\'\\nfrom 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\'>]

 

以上是关于面向对象之继承与派生的主要内容,如果未能解决你的问题,请参考以下文章

python之旅:面向对象之继承与派生

面向对象之继承与派生(day7)

面向对象之继承与派生

面向对象之继承与派生

DAY7-面向对象之继承与派生

python面向对象之继承与派生