3.1.2 继承与派生 在子类中重用父类的方法

继承是类与类之间的关系, 什么是什么

总结对象之间的相似点成类

总结类之间的相似点成父类

类从(子)下到(父)上,是抽象。类从上到下,是继承,类到对象实例化。

 

# class ParentClass1:
#     pass
#
# class ParentClass2:
#     pass
#
# class SubClass1(ParentClass1):
#     pass
#
# class SubClass2(ParentClass1,ParentClass2):
#     pass
#
# print(SubClass1.__bases__)
# print(SubClass2.__bases__)



class Hero:
    x=3
    def __init__(self,nickname,life_value,aggresivity):
        self.nickname=nickname
        self.life_value=life_value
        self.aggresivity=aggresivity
    def attack(self,enemy):
        enemy.life_value-=self.aggresivity

class Garen(Hero):
    # x=2
    pass

class Riven(Hero):
    pass


g1=Garen('刚们',29,30)
# print(g1.nickname,g1.life_value,g1.aggresivity)
# g1.x=1

print(g1.x)



#属性查找小练习
class Foo:
    def f1(self):
        print('from Foo.f1')

    def f2(self):
        print('from Foo.f2')
        self.f1() #b.f1()  继续从对象本身开始找

class Bar(Foo):
    def f1(self):
        print('from Bar.f1')

b=Bar()
# print(b.__dict__)
b.f2()

输出:
from Foo.f2
from Bar.f1

  

属性查找: 先从对象自己这里查找,没有再去类里去找,类再没有,去父类找。

 

 

派生:

       子类可以添加自己新属性或者在自己这里重新定义这些属性(不会影响父类),

需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新的属性时,就以自己为准了。

class Hero:
    def __init__(self,nickname,life_value,aggresivity):
        self.nickname=nickname
        self.life_value=life_value
        self.aggresivity=aggresivity
    def attack(self,enemy):
        enemy.life_value-=self.aggresivity

class Garen(Hero):
    camp='Demacia'

    def attack(self,enemy):
        print('from Garen Class')

class Riven(Hero):
    camp='Noxus'


g=Garen('草丛伦',100,30)
r=Riven('锐雯雯',80,50)

# print(g.camp)
# g.attack(r)
# print(r.life_value)

g.attack(r)

 

 

继承的实现原理:

   python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

 

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

 1. 子类会先于父类检查

 2  多个父类会根据它们在列表中的顺序被检查

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

 新式类中每个类都有一个mro方法。可以查出继承的顺序。

但经典类没有mro这个方法。

  如:f = F()

      print(f.__mro__) #只有新式类才有

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

 

Python2中有分经典类和新式类。Python3都是新式类。

python2中 经典类: 没有继承object类以及子类,都称为经典类。这个类的子类也叫经典类。

class Foo:
    pass

class Bar(Foo):
    pass

python2:新式类: 继承了object类以及子类,都称为新式类。

class Foo(object):
    pass

class Bar(Foo):
    pass
print(Bar.__base__)

  

Python3:默认都继承了object类

class Foo():
    pass

print(Foo.__base__)

输出:<class: object>

  

当类是经典类时,多继承情况下,在要查找属性不存在时,会按照深度优先的方式查找下去。

 

#验证多继承情况下的属性查找

class A:
    # def test(self):
    #     print('from A')
    pass

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,D,B,E,C,A

print(F.mro())
# f=F()
# f.test()

  

 

 

实验:如果C没有继承A,新式类会不会是F D B A E C ??

 

在子类中重用父类的方法

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

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

方式二:super() (依赖继承)

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是调用普通函数的方式,即:类名.func(),此时就与调用函数无异了,因此即便是self参数也要为其传值 。

方式一示例:

class Hero:
    def __init__(self,nickname,life_value,aggresivity):
         self.nickname=nickname
         self.life_value=life_value
         self.aggresivity=aggresivity
   def attack(self,enemy):
         enemy.life_value-=self.aggresivity

class Garen(Hero):
     camp='Demacia'

     def __init__(self,nickname,life_value,aggresivity,weapon):
         # self.nickname=nickname
         # self.life_value=life_value
         # self.aggresivity=aggresivity
         Hero.__init__(self,nickname,life_value,aggresivity)  #也是指名道姓,不依赖继承

         self.weapon=weapon

     def attack(self,enemy):
         Hero.attack(self,enemy) #指名道姓,不依赖于继承。如果没有继承Hero也是可以的。
         print('from Garen Class')


 g=Garen('草丛伦',100,30,'金箍棒')

 print(g.__dict__)

  方式二示例:

 class Hero:
#     def __init__(self,nickname,life_value,aggresivity):
#         self.nickname=nickname
#         self.life_value=life_value
#         self.aggresivity=aggresivity
#     def attack(self,enemy):
#         enemy.life_value-=self.aggresivity
#
#
# class Garen(Hero):
#     camp='Demacia'
#
#     def __init__(self,nickname,life_value,aggresivity,weapon):
#         # self.nickname=nickname
#         # self.life_value=life_value
#         # self.aggresivity=aggresivity
#
#         # super(Garen,self).__init__(nickname,life_value,aggresivity) #python2
#         super().__init__(nickname,life_value,aggresivity) #python3可以这样简写
#         self.weapon=weapon
#
#     def attack(self,enemy):
#         super(Garen,self).attack(enemy) #依赖继承, super(Garen,self)是对象,所以self参数不用传。
#         print('from Garen Class')
#
#
# g=Garen('草丛伦',100,30,'金箍棒')
#
# print(g.__dict__)

  super()沿着MRO列表,是基于调用对象的MRO往后找。而不是调用对象本身再开始找。

class A:
    def f1(self):
        print('from A')
        super().f1()  #c对象有MRO往后找。


class B:
    def f1(self):
        print('from B')

class C(A,B):
    pass


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


c=C()
c.f1()
输出:
from A
from B

  

posted @ 2018-05-29 18:18  beallaliu  阅读(324)  评论(0编辑  收藏  举报