(二十二)类与对象 ---- 继承

Posted xulan0922

tags:

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

什么时候用继承?

1.当类之间有显著不同,且较小的类是较大的类的组件时,用组合比较好

比如机器人类是一个大类,躯干类、胳膊类、腿类是小类

2.当类之间有很多相同的属性与功能,提取这些共性作为基类,用继承比较好

比如鸡和鸭,我们可以提取他们的共性(两只翅膀、用两只脚走路)做一个禽类,但是他们也有各自独特的个性(鸡会打鸣,鸭会游泳)

class qing():                                 #共性提取为一个基类(禽类)
    wing = 2                                  #父类数据属性
    def __init__(self,owner):          
        self.owner = owner
    def walk(self):                           #父类函数属性
        print(%s用两只脚走路 %self.owner)

class ji(qing):                               #鸡类继承禽类
    def da_ming(self):                        #子类有自己的函数属性
        print(%s会打鸣 %self.owner)

class ya(qing):                               #鸭类继承禽类
    def you_yong(self):                       #子类有自己的函数属性
        print(%s会游泳 %self.owner)

j1 = ji(小王家的鸡)                           #鸡类实例化
j1.da_ming()                                  #可以调用实例自己的函数属性
j1.walk()                                     #可以调用实例继承来的函数属性

y1 = ya(小明家的鸭)                           #鸭类实例化
y1.you_yong()                                 
y1.walk()
结果:

小王家的鸡会打鸣
小王家的鸡用两只脚走路
小明家的鸭会游泳
小明家的鸭用两只脚走路

继承有两种含义

含义一:子类继承基类的方法,并且做出自己的改变或扩展(代码重用),如上禽类的例子

含义二:基类是一个接口类,子类继承这个接口类,并实现接口中定义的方法

实践中,继承的第一种含义意义并不大,甚至是有害的,因为这种方式使得子类与基类出现强耦合

继承的第二种含义就很重要了,它又叫“接口继承”

接口继承实质上做了一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可以一视同仁的处理实现了接口的所有对象——程序设计上,这叫归一化

就像linux的泛文件概念(一切皆文件),所有东西都可以当文件处理,不必关心它是内存、磁盘还是网络(然后在内存、磁盘和网络的子类中,再去做针对性的底层设计,即具体实现接口类中定义的接口)

如何强制子类必须实现基类中某些指定的方法和属性,否则就抛异常?python的abc模块就满足了这个需求。

import abc
class All_file(metaclass=abc.ABCMeta):          #abc.ABCMeta是一个用于实现抽象类的一个基础类
    @abc.abstractmethod                         #加上@abstractmethod之后,如果需要用到的这个方法则必须用新的方法将其实现
    def read(self):                             #子类必须实现读方法
        pass                                    #接口类不用实现逻辑,只是用来规范子类
    @abc.abstractmethod                         #子类必须实现写方法
    def write(self):
        pass

class Disk(All_file):
    def read(self):
        print(disk read)

    def write(self):
        print(disk write)

class Cdrom(All_file):
    def read(self):
        print(cdrom read)

    def write(self):
        print(cdrom write)

class Mem(All_file):
    def read(self):
        print(mem read)

    def write(self):                     #如果这个write方法不写,将报错:TypeError: Can‘t instantiate abstract class Mem with abstract methods write
        print(mem write)

m1=Mem()
m1.read()                     #mem read
m1.write()                    #mem write

1.接口类的作用只是用来规范子类,所以他不用实现逻辑,写个pass就ok

2.接口类不需要被实例化,也没有这个必要

3.子类如果继承了接口类,就一定要实现接口类中加了@abstractmethod的方法

继承的顺序

python的类可以继承多个类,如果继承多个类,那么其继承顺序的方式有:深度优先和广度优先
基类或者父类继承了object类,那么该类就是新式类,否则便是经典类
注意:python3中统一都是新式类,pyhon2中才分新式类与经典类
当类是经典类时,多继承情况下,会按照深度优先方法查找
当类时新式类时,多继承情况下,会按照广度优先方法查找

class A:
    def test(self):
        print(A)
    pass
class B(A):
    def test(self):
        print(B)
    pass
class C(A):
    def test(self):
        print(C)
    pass
class D(B):
    def test(self):
        print(D)
    pass
class E(C):
    def test(self):
        print(E)
    pass
class F(D,E):
    def test(self):
        print(F)
    pass
f1=F()                   #python3都是新式类,广度优先,所以继承顺序是:F--D--B--E--C--A
f1.test()   

print(F.__mro__)  #(<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>)

B.C继承A,D继承B,E继承C,F继承D,E,我们看下F的继承顺序:

技术图片

如果是新式类,继承的顺序是:F--D--B--E--C--A,如图中绿线所示,左边的线不会找到头,这就是所谓的广度优先

如果是经典类,继承的顺序是:F--D--B--A--E--C,如图中红线所示,左边的线直接找到头,这就是所谓的深度优先

Python到底是如何实现继承的?

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

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

MRO列表遵循以下三条准则:

  • 子类会先于父类被检查;
  • 多个父类会根据它们在列表中的顺序被检查;
  • 如果对下一个类存在两个合法的选择,则选择第一个类;

注意:只有新式类才有.__mro__这个属性,可以查看线性列表,经典类没有这个属性

 在子类中调用父类方法

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def run(self):
        print(%s 在跑!%self.name)

class China(People):
    def __init__(self,name,age,country):
        People.__init__(self,name,age)                  #子类中调用父类的__init__方法,可以用super().__init__(name,age) 代替,注意用super()调用父类的方法时不用传self
        self.country = country
    def run(self):
        People.run(self)                                #子类中调用父类的run方法  可以用super().run()代替
        print(%s人 %s 在跑!%(self.country,self.name))

p2 = China(Tom,26,中国)
p2.run()

 

以上是关于(二十二)类与对象 ---- 继承的主要内容,如果未能解决你的问题,请参考以下文章

C#编程(二十二)----------继承的类型

第二十六天 组合 封装 多态

java 面向对象(二十二):关键字:final

Java入门二十二 面向对象三大特征之多态

《Android源代码设计模式解析与实战》读书笔记(二十二)

友元超英雄(二十二)