python-类继承与重用

类的继承与重用

一、什么是继承?

  1、继承是类与类间的关系。

  2、是一种什么‘’是‘’什么的关系。

  3、继承功能就是用来解决代码重用的问题。

1、继承: 是一种创建新类的方式,

  python中,

  1、新建的类可以继承一个或多个父类。

  2、父类可以成为基类或超类。

  3、所新建的类称为子类或派生类。

2、类继承的数量:

  python中类的继承分为:继承一个或多个。

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass
类继承1个或多个父类

3、查看继承:

  python中可以通过__base__命令可以查看

SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
#(<class '__main__.ParentClass1'>,)
SubClass2.__bases__
#(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
__base__方法查看继承了什么类

4、如果没有指定基类,python的类会默认继承object类,因为object类是所有python类的基类。

 ParentClass1.__bases__ #由于ParentClass1这个类中没有继承类,python默认会继承object类,打印如下:

 #(<class 'object'>,)

5、经典类与新式类,及其区别。

  1、python2——才有经典与新式的区分。python3——统一都是新式类。

  2、python2——没有显式继承object的类的,以及该类的子类,都是经典类。声明了object的类及子类,都是新式类。

  3、python3——无论是否继承object,都默认继承object,因为python3所有的类都是新式类。

  4、经典类与新式类的主要区别在:

     经典类——多继承的情况,要查找的属性不存在时,按照深度优先。

     新式类——。。。。。。。按照广度优先。

二、继承与抽象(先抽象再继承)

   抽象——抽取类似或相似的部分。

   继承——是抽象后的结果,通过编程来实现,一定要先有抽象的过程,才能有继承的结果。

抽象的过程——提取对象相似的特征构造成类,然后通过类,再抽象成这些类的父类。

继承的过程如下图。

三、继承与重用性

  我们定义了一个类称A,再定义一个新类B,如果B这个类中有大部分的内容与A相同时。

  我们就可以用到类的继承的概念。通过继承,B就会遗传A所有的属性(包括数据和函数),从而实现代码重用。

class Hero:
    def __init__(self,nickname,aggressivity,life_value):
        self.nickname=nickname
        self.aggressivity=aggressivity
        self.life_value=life_value

    def move_forward(self):
        print('%s move forward' %self.nickname)

    def move_backward(self):
        print('%s move backward' %self.nickname)

    def move_left(self):
        print('%s move forward' %self.nickname)

    def move_right(self):
        print('%s move forward' %self.nickname)

    def attack(self,enemy):
        enemy.life_value-=self.aggressivity
class Garen(Hero):
    pass

class Riven(Hero):
    pass

g1=Garen('草丛伦',100,300)
r1=Riven('锐雯雯',57,200)

print(g1.life_value) #结果:300
r1.attack(g1)
print(g1.life_value) #结果:243
通过继承实现代码重用

注意:1、用已有的类建立新的类,可大大节省代码。

     2、 我们也可以继承被人的类如标准库,来定制新的类型。

四、属性查找 

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

    def f2(self):  # 4、找到f2了执行命令
        print('Foo.f2')
        self.f1()  # 5、因为self 指的是对象本身,所以是b.f1()
        # 6、从bar()里找到f1() 所以打印Bar.f1


class Bar(Foo):  # 3、在自己的类里面也没找到,所以在父类FOO里找
    def f1(self):
        print('Bar.f1')


b = Bar()  # 2、从自己的类里面查找f1()
b.f2()  # 1、从对象b里查找f1(),由于没有定义__init__方法,所以找不到。
属性查找顺序

查找说明:

  1、程序实例化了b=Bar()这个对象。

  2、b.f2()这个方法在b 这个类里找不到,就找从FOO这个类里找.

  3、在父类FOO中找到了f2()方法并执行。

  4、当执行self.f1()时,由于self指的是类本身即Bar,所以执行Bar下的f1()。

五、派生

类可以继承父类的属性,也可以自己新属于自己的属性,一旦定义的属性名与父类的重名,调用的时候以自己的为准。

 

# 派生
class Hero:
    def __init__(self, nickname, aggressivity, life_value):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value

    def move_forward(self):
        print('%s move forward' % self.nickname)

    def move_backward(self):
        print('%s move backward' % self.nickname)

    def move_left(self):
        print('%s move forward' % self.nickname)

    def move_right(self):
        print('%s move forward' % self.nickname)

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity


class Riven(Hero):
    camp = 'Noxus'

    def attack(self, enemy):  # 在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        print('from riven', enemy)

    def fly(self):  # 在自己这里定义新的
        print('%s is flying' % self.nickname)


r1 = Riven('锐雯雯', 57, 200)
print(r1.attack(10))
派生用法

 

注意:在子类中,如果新建了与父类重名的函数属性,在调用时也应该按照函数调用方式进行,即有多少个参数应传多少个值

六、继承的实现原理

Python实现继承的原理是在用户定义类的时候,计算并生成一个MRO线性列表。主要有如下原则:

  1、子类优先于父类查找

  2、多个父类会根据MRO列表顺序查找

  3、如果下一个类对应两个合法选项,第一个父类优先。

注意:C和java只支持继承一个父类,python支持继承多个父类,如果继承多个父类,查找方式有两种:

  经典类——深度优先:

  新式类——广度优先

在python3查找顺序练习:

 

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):
    def test(self):
        print('from D')


class E(C):
    def test(self):
        print('from E')


class F(D, E):
    # def test(self):
    #     print('from F')
    pass


f1 = F()
f1.test()  # F D B E C A

print(F.__mro__)  # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性

# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
# python3中统一都是新式类
# pyhon2中才分新式类与经典类
py3多个父类查找方式

 

由于py3的类统一是新式类,按照广度优先查找顺序,所以查找顺序为F=>D=>B=>E=>C=>A

如果是在py2,是经典类,按深度优先,所以是这样的F=>D=>B=>A=>E=>C

七、子类中调用父类的方法

  有两种方法:

  1、指名道姓调用

# 1、指名道姓方式引用,这种方式跟继承没有关系,只是简单的类的函数属性的引用
class Vehicle:  # 定义交通工具类
    Country = 'China'

    def __init__(self, name, speed, load, power):
        self.name = name
        self.speed = speed
        self.load = load
        self.power = power

    def run(self):
        print('开动啦...')


class Subway(Vehicle):  # 地铁
    def __init__(self, name, speed, load, power, line):
        Vehicle.__init__(self, name, speed, load, power) #指名道姓引用类的函数属性,
                                                         # 相当于引用了类的函数属性,需要什么参数要传什么参数
        self.line = line                                 # 新增子类属性

    def run(self):
        print('地铁%s号线欢迎您' % self.line)
        Vehicle.run(self)


line13 = Subway('中国地铁', '180m/s', '1000人/箱', '', 13)
line13.run()
指名道姓引用类函数属性

注意:这样的调用方式,跟继承没有关系,相当于引用了类的函数属性,需要什么参数就要传什么参数。

  2、super()方法调用

# 调用方法二:super()
#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
    def test(self):
        print('test A')
        super().test()
class B:
    def test(self):
        print('from B')
class C(A,B):
    # print('test c')
    pass

c=C()
c.test() #打印结果:from B
Super()方法调用

注意:用super方法调用,是依赖于继承的,即使两个父类间没有继承关系,都会从子类的继承关系的MRO表继续往后找。

posted @ 2018-05-05 23:52  OYxing  阅读(845)  评论(1编辑  收藏  举报