面向对象的继承和派生

Posted linyuhong

tags:

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

一、继承

1、什么是继承

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

子类会“”遗传”父类的属性,从而解决代码重用问题(比如练习7中Garen与Riven类有很多冗余的代码)

2、python中类的继承分为:单继承和多继承

技术分享图片
class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

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

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

3、查看继承

技术分享图片
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class __main__.ParentClass1>,)
>>> SubClass2.__bases__
(<class __main__.ParentClass1>, <class __main__.ParentClass2>)
View Code

4、经典类与新式类

技术分享图片
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
#关于新式类与经典类的区别,我们稍后讨论
View Code

二、派生

当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

class Riven(Hero):
    camp=Noxus
    def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        print(from riven)
    def fly(self): #在自己这里定义新的
        print(%s is flying %self.nickname)

三、组合与重用性

组合:类与类之间没有关联,但又想把它们组合到一起

技术分享图片
>>> class Equip: #武器装备类
...     def fire(self):
...         print(release Fire skill)
... 
>>> class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
...     camp=Noxus
...     def __init__(self,nickname):
...         self.nickname=nickname
...         self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
... 
>>> r1=Riven(锐雯雯)
>>> r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
release Fire skill
View Code

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同

1.继承的方式

通过继承建立了派生类与基类之间的关系,它是一种‘是‘的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

2.组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

四、接口与归一化设计

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

例如我们定义一个硬盘 ALLFile 的类,其他子类可以通过继承它实现具体的功能。但是如果我们只是单纯的写成下面这样:

class AllFile():
    def read(self):
        pass

    def write(self):
        pass


class Disk(AllFile):
    def read(self):
        print("read Disk")

disk = Disk()

这样虽然我们可以继承,但我们不用必须去实现父类所有的方法,普通的继承达不到一种我们想要的约束的作用,这时改成

import abc

class
AllFile(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass class Disk(AllFile): def read(self): print("read Disk") def write(self): print("write Disk") disk = Disk()

注意,父类只提供接口,具体实现逻辑在子类里面

归一化的好处在于:

1. 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

2. 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

五、继承实现的原理

1、子类查找父类的顺序

分别是:深度优先和广度优先

技术分享图片

技术分享图片

技术分享图片
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()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

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

继承顺序
继承顺序

2、继承原理(python如何实现的继承)

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

>>> F.mro() #等同于F.__mro__
[<class __main__.F>, <class __main__.D>, <class __main__.B>, <class __main__.E>, <class __main__.C>, <class __main__.A>, <class object>]

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

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

方法一:即父类名.父类方法()

技术分享图片
#_*_coding:utf-8_*_
__author__ = Linhaifeng

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()
View Code

方法二:super()

技术分享图片
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):
        #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(name,speed,load,power)
        self.line=line

    def run(self):
        print(地铁%s号线欢迎您 %self.line)
        super(Subway,self).run()

class Mobike(Vehicle):#摩拜单车
    pass

line13=Subway(中国地铁,180m/s,1000人/箱,,13)
line13.run()
View Code

注意:super()调用的时候不用传self

注意:即使没有直接继承关系,super仍然会按照mro继续往后查找

技术分享图片
#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
    def test(self):
        super().test()
class B:
    def test(self):
        print(from B)
class C(A,B):
    pass

c=C()
c.test() #打印结果:from B


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

 

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

面向对象之继承与派生

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

面向对象-继承与派生

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

面向对象之继承与派生

python 面向对象基础梳理三 继承派生与组合