lesson 042 —— 继承

lesson 042 —— 继承

什么是继承?

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

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

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

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

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

查看继承

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

注意:

  1. 只有在python2中才分新式类和经典类,python3中统一都是新式类
  2. 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类;显式地声明继承object的类,以及该类的子类,都是新式类
  3. python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

如果没有指定基类,python的类会默认继承 object 类,object 是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

>>> ParentClass1.__bases__
(<class ‘object‘>,)
>>> ParentClass2.__bases__
(<class ‘object‘>,)

继承与抽象

继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承。

抽象即抽取类似或者说比较像的部分。抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

继承与重用性

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。 通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。

派生

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

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

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 __init__(self,nickname,aggressivity,life_value,skin):
        Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能
        self.skin=skin #新属性
    def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        Hero.attack(self,enemy) #调用功能,传入self参数,即这里使用self这个对象
        print(‘from riven‘)
    def fly(self): #在自己这里定义新的
        print(‘%s is flying‘ %self.nickname)

r1=Riven(‘锐雯雯‘,57,200,‘比基尼‘)
r1.fly()
print(r1.skin)

‘‘‘
运行结果
锐雯雯 is flying
比基尼
‘‘‘

组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合。

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

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

  1. 继承的方式

    通过继承建立了派生类与基类之间的关系,它是一种‘是‘的关系,比如白马是马,人是动物。当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好.

  2. 组合的方式

    用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好.

接口与归一化设计

1. 接口的特性
  • 是一组功能的集合,而不是一个功能
  • 接口的功能用于交互,所有的功能都是public,即别的对象可操作
  • 接口只定义函数,但不涉及函数实现
  • 这些功能是相关的
2. 为什么用接口

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处:

  • 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
  • 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
3. 模仿 interface

在python中根本就没有一个叫做interface的关键字,如果非要去模仿接口的概念,可以借助第三方模块:

zope.interface,可以参考文档设计模式

也可以使用继承。继承的两种用途:

  1. 继承基类的方法,并且做出自己的改变或者扩展(代码重用):实践中,继承的这种用途意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
  2. 声明某个子类兼容于某基类,定义一个接口类(模仿java的Interface),接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
    def read(self): #定接口函数read
        pass

    def write(self): #定义接口函数write
        pass

class Txt(Interface): #文本,具体实现read和write
    def read(self):
        print(‘文本数据的读取方法‘)

    def write(self):
        print(‘文本数据的读取方法‘)

class Sata(Interface): #磁盘,具体实现read和write
    def read(self):
        print(‘硬盘数据的读取方法‘)

    def write(self):
        print(‘硬盘数据的读取方法‘)

class Process(Interface):
    def read(self):
        print(‘进程数据的读取方法‘)

    def write(self):
        print(‘进程数据的读取方法‘)

上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,这就用到了抽象类。

抽象类

1. 什么是抽象类?

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。

2. 为什么要有抽象类

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的。

3. Python 中实现抽象类
#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type=‘file‘
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        ‘子类必须定义读功能‘
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        ‘子类必须定义写功能‘
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print(‘文本数据的读取方法‘)

    def write(self):
        print(‘文本数据的读取方法‘)

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print(‘硬盘数据的读取方法‘)

    def write(self):
        print(‘硬盘数据的读取方法‘)

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print(‘进程数据的读取方法‘)

    def write(self):
        print(‘进程数据的读取方法‘)

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
4. 抽象类与接口

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

继承实现的原理

1. 继承顺序

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)。

如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性。

如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先。

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会计算出一个方法解析顺序(MRO)列表,这个 MRO(method resolution order) 列表就是一个简单的所有基类的线性顺序列表,例如:

>>> 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列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个 MRO 列表的构造是通过一个 C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则:

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

子类调用父类的方法

方法一:指名道姓,即 父类名.父类方法()
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()
方法二: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()

强调: 二者使用哪一种都可以,但最好不要混用。

即使没有直接继承关系,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‘>]

两种调用父类方法的区别:

当你使用 super() 函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表


#指名道姓
class A:
    def __init__(self):
        print(‘A的构造方法‘)
class B(A):
    def __init__(self):
        print(‘B的构造方法‘)
        A.__init__(self)

class C(A):
    def __init__(self):
        print(‘C的构造方法‘)
        A.__init__(self)

class D(B,C):
    def __init__(self):
        print(‘D的构造方法‘)
        B.__init__(self)
        C.__init__(self)

    pass
f1=D() #A.__init__被重复调用
‘‘‘
D的构造方法
B的构造方法
A的构造方法
C的构造方法
A的构造方法
‘‘‘

#使用super()
class A:
    def __init__(self):
        print(‘A的构造方法‘)
class B(A):
    def __init__(self):
        print(‘B的构造方法‘)
        super(B,self).__init__()

class C(A):
    def __init__(self):
        print(‘C的构造方法‘)
        super(C,self).__init__()

class D(B,C):
    def __init__(self):
        print(‘D的构造方法‘)
        super(D,self).__init__()

f1=D() #super()会基于mro列表,往后找
‘‘‘
D的构造方法
B的构造方法
C的构造方法
A的构造方法
‘‘‘

原文地址:https://www.cnblogs.com/arelive/p/python-42.html

时间: 2024-10-01 20:41:27

lesson 042 —— 继承的相关文章

Asp.Net 初级 高级 学习笔记

本文并非作者原创,但是作者发表! 不知道十年后,会用多少人收益我的文章,哈哈! 各位,来加个关注![注*]改文章总结了ASP.Net从入门到高级的知识! 朱磊:2012 - 0212 -------------- [朱磊是本文的原创大神,据说当年在黑马刚毕业,月薪税前1W]-------------01.Main函数是什么?在程序中使用Main函数有什么需要注意的地方?02.CLR是什么?程序集是什么?当运行一个程序集的时候,CLR做了什么事情?03.值类型的默认值是什么?(情况一:字段或全局静

.Net 面试题整理(一)

.Net 面试题整理(一) 好久的资料了,拿出来分享给大家,主要是记录一些面试题整理. Main函数是什么?在程序中使用Main函数有什么需要注意的地方? Q:程序的入口函数!函数名字不能改变:一个程序中有且只有一个Main函数. CLR是什么?程序集是什么?当运行一个程序集的时候,CLR做了什么事情? Q:Common Language RunTime 公共语言运行时. 编译后代码的集合.(包括exe和dll). 加载项目里所有的类到内存,并找到其中的主函数,并作为默认的启动函数调用执行. 值

Lesson 3 上机练习题——继承

–        在包bzu.aa中定义一个交通工具类(Vehicle): n  属性--载客量(capacity) n  方法 u  无参构造方法(给capacity初始化值为2,并输出"执行交通工具类的无参构造方法.") u  有参构造方法(传参给capacity初始化,并输出"执行交通工具的有参构造方法.") u  capacity的set.get方法 u  print方法:输出capacity –        在包bzu.aa中定义一个汽车类(Car)继承

[小北De编程手记] : Lesson 05 - Selenium For C# 之 API 下

上一篇,我们介绍了一些Selenium WebDriver相关的API,下面我们就接着上一篇继续介绍Selenium常用的API,这一篇的内容主要涉及到以下话题: Selenium API:复杂事件处理 Selenium API:特殊DOM元素处理 Selenium API:截图功能 Selenium API:关于框架扩展 (一)Selenium API:复杂事件处理 首先,我们试想一下这样的场景.待测试的系统支持一些组合键的操作,例如:按住Ctrl的同时点击某个表格的某个单元格,该数据行会高亮

python3 面向对象、类、继承、组合、派生、接口、子类重用父类方法

对象是特征(变量)与技能(函数)的结合体而类是一系列对象共同的特征与技能的集合体 class teacher: lesson = "python" def __init__(self,name,color,age): # 只干初始化的活 self.name = name if not isinstance(name,str): raise TypeError self.color = color self.age = age def jineng(self): print('新技能')

Lesson 39-40 Do you like your job?

一 Words 1 ought to 应该v 2 occupation 职业n,占用v eg. He is a teacher by occupation but a philosopher by inclination倾向,爱好.他的职业是教师,但他的本心是成为哲学家. 3 job satisfaction 工作满足够n eg. I doubt I'll ever get rich,but I get job satisfaction.我想我永远都不会富有,但是我在工作上很有满足感 4 fri

[小北De编程手记] : Lesson 04 玩转 xUnit.Net 之 Fixture(下)

上一篇文章<[小北De编程手记] : Lesson 03 玩转 xUnit.Net 之 Fixture(上)>向大家介绍了xUnit.Net 共享数据的方式.Test Case的构造函数 & IDisposable.Dispose.Class级别的Fixture : IClassFixture.这一篇,我们接着讲解后面的内容,回顾一下本文要讨论的内容: xUnit.Net 共享数据的方式(上) Test Case的构造函数 & IDisposable.Dispose(上) Cl

udacity android 实践笔记: lesson 4 part b

udacity android 实践笔记: lesson 4 part b 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 联系:1307316一九六八(短信最佳) 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处. tips:https://code.csdn.net/titer1/pat_aha/blob/mast

C++面向对象编程之继承

一. 封装和继承 1.1 封装的概念 封装:通过访问控制属性对类类型中的属性和行为进行打包和限制. 1.2 继承:通过一种机制表达出类型之间的共性和特性的方式. 继承基本语法: class 子类名 : 继承方式1 父类1,继承方式2 父类2... { 类的定义 }; 继承方式: public  公有方式 private 私有方式 protected 保护方式 1.3 继承的特性 (1) 一个子类类型的对象在任何时候都可以看作基类类型的对象(心里始终要记着). (2) 一个父类类型的对象作为子类对