Python学习日记(二十四) 继承
继承
什么是继承?就是一个派生类(derived class)继承基类(base class)的字段和方法。一个类可以被多个类继承;在python中,一个类可以继承多个类。
父类可以称为基类和超类,而子类可以称为派生类
在继承中可分为单继承和多继承两种
下面是继承的用法,语法为'class 子类的名字(父类名):'
class Plane: #定义一个所有战机的父类
def __init__(self,name,speed,hp,atk):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk class Fighter(Plane): #定义一个Fighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,money):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk
self.money= money
def Attack(self,enemyFighter):
enemyFighter.hp -= self.atk class EnemyFighter(Plane): #定义一个EnemyFighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,type):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk
self.type = type
def EnemyAttack(self,fighter):
fighter.hp -= self.atk
我们如果想知道一个类的父类是谁,可以使用__bases__方法查看
print(Plane.__bases__) #(<class 'object'>,)
print(Fighter.__bases__) #(<class '__main__.Plane'>,)
print(EnemyFighter.__bases__) #(<class '__main__.Plane'>,)
可以从结果看出两个子类都继承了Plane这个父类,而Plane类它继承的是类的'祖宗'object类。在一个python3里所有的类都有父类,如果一个类它没有发生继承那么它的父类就是object的子类。
新式类:没有继承父类默认继承object类
抽象的概念
抽象就是抽取类似或比较像的部分
分为两个层次:将两个比较相似的对象比较像的部分抽取成类和把多个类比较像的部分抽取成父类
继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式表达出抽象的结构;且类与类之间才有继承的关系
单继承
我们在写上面的代码时候可以发现Fighter类和EnemyFighter类中有很多属性在父类都是重复的,并且有些属性又是自己特有的,那么对于这个派生类特有的属性我们称为派生属性。下面我们修改我们上面的代码:
class Plane: #定义一个所有战机的父类
def __init__(self,name,speed,hp,atk):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk class Fighter(Plane): #定义一个Fighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,money):
Plane.__init__(self,name,speed,hp,atk) #这里的self是Fighter的self
self.money= money #派生属性
def Attack(self,enemyFighter):
enemyFighter.hp -= self.atk class EnemyFighter(Plane): #定义一个EnemyFighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,type):
Plane.__init__(self,name,speed,hp,atk) #这里的self是EnemyFighter的self
self.type = type #派生属性
def EnemyAttack(self,fighter):
fighter.hp -= self.atk f1 = Fighter('player1',150,1000,100,500)
print(f1.__dict__) #{'name': 'player1', 'speed': 150, 'hp': 1000, 'atk': 100, 'money': 500}
Boss1 = EnemyFighter('AKS-89',50,3000,500,'BOSS')
print(Boss1.__dict__) #{'name': 'AKS-89', 'speed': 50, 'hp': 3000, 'atk': 500, 'type': 'BOSS'}
现在给Plane类添加一个方法Attack,当如果子类和父类的方法重名时,在子类在调用的时候,如果子类中有这个名字那么就一定是用子类的,子类没有才找父类的,如果父类没有就报错
class Plane: #定义一个所有战机的父类
def __init__(self,name,speed,hp,atk):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk
def Attack(self):
print(self.name+'发射子弹!') class Fighter(Plane): #定义一个Fighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,money):
Plane.__init__(self,name,speed,hp,atk) #这里的self是Fighter的self
self.money= money #派生属性
def Attack(self,enemyFighter):
enemyFighter.hp -= self.atk
print('Now {0} hp : {1}'.format(enemyFighter.name,enemyFighter.hp)) class EnemyFighter(Plane): #定义一个EnemyFighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,type):
Plane.__init__(self,name,speed,hp,atk) #这里的self是EnemyFighter的self
self.type = type #派生属性
def EnemyAttack(self,fighter):
fighter.hp -= self.atk
print('Now {0} hp : {1}'.format(fighter.name, fighter.hp)) f1 = Fighter('player1',150,1000,100,500)
Boss1 = EnemyFighter('AKS-89',50,3000,500,'BOSS') f1.Attack(Boss1) #Now AKS-89 hp : 2900
Boss1.EnemyAttack(f1) #Now player1 hp : 500
Boss1.Attack() #AKS-89发射子弹!
派生方法:父类中没有但在子类中特有的方法,例如上面的EnemyAttack()
如果一个子类还想用父类的东西,应该单独调用父类的
<1>父类名.类方法名(self参数),这里的self参数必须传
class Fighter(Plane): #定义一个Fighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,money):
Plane.__init__(self,name,speed,hp,atk) #这里的self是Fighter的self
self.money= money #派生属性
def Attack(self,enemyFighter):
Plane.Attack(self) #如果既想实现新的功能也想使用父类原本的功能,还需要在子类中调用父类
enemyFighter.hp -= self.atk
print('Now {0} hp : {1}'.format(enemyFighter.name,enemyFighter.hp))
f1 = Fighter('player1',150,1000,100,500)
Boss1 = EnemyFighter('AKS-89',50,3000,500,'BOSS')
f1.Attack(Boss1) #player1发射子弹!
#Now AKS-89 hp : 2900
<2>super方法
class Plane: #定义一个所有战机的父类
def __init__(self,name,speed,hp,atk):
self.name = name
self.speed = speed
self.hp = hp
self.atk = atk
def Attack(self):
print(self.name+'发射子弹!') class Fighter(Plane): #定义一个Fighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,money):
super().__init__(name,speed,hp,atk) #这里的self是Fighter的self
self.money= money #派生属性
def Attack(self,enemyFighter):
Plane.Attack(self) #如果既想实现新的功能也想使用父类原本的功能,还需要在子类中调用父类
enemyFighter.hp -= self.atk
print('Now {0} hp : {1}'.format(enemyFighter.name,enemyFighter.hp)) class EnemyFighter(Plane): #定义一个EnemyFighter类 它继承的是Plane类
def __init__(self,name,speed,hp,atk,type):
super().__init__(name,speed,hp,atk) #这里的self是EnemyFighter的self
self.type = type #派生属性
def EnemyAttack(self,fighter):
Plane.Attack(self)
fighter.hp -= self.atk
print('Now {0} hp : {1}'.format(fighter.name, fighter.hp)) f1 = Fighter('player1',150,1000,100,500)
Boss1 = EnemyFighter('AKS-89',50,3000,500,'BOSS')
f1.Attack(Boss1) #player1发射子弹!
#Now AKS-89 hp : 2900
Boss1.EnemyAttack(f1) #AKS-89发射子弹!
#Now player1 hp : 500
super()函数在这里省略了两个参数,分别是子类名和self参数。super()只在新式类有并且它只在python3存在,而在python3中所有的类都是新式类。对于单继承来说super()就可以找到他的父类了;上面的super()用法是在类的内部使用。
super()在类的外部使用:
f1 = Fighter('player1',150,1000,100,500)
Boss1 = EnemyFighter('AKS-89',50,3000,500,'BOSS')
super(Fighter,f1).Attack() #player1发射子弹!
super(EnemyFighter,Boss1).Attack() #AKS-89发射子弹!
可以直接找父类的这一个函数并进行调用
在单继承中,一个类它只继承一个基类且一般来说它能够减少代码的重复,提高代码可读性,规范编程模式
多继承
多继承顾名思义就是一个类它继承了两个或两个以上的父类
<1>钻石继承:
假设有4个类它们的继承关系如下图表示

class A:
def fuc(self):
print('A')
class C(A):
def fuc(self):
print('C')
class D(A):
def fuc(self):
print('D')
class B(C,D):
def fuc(self):
print('B')
b = B()
b.fuc() #B
第一次执行fuc
如果把B类的方法注释掉现在的结果是什么?
class A:
def fuc(self):
print('A')
class C(A):
def fuc(self):
print('C')
class D(A):
def fuc(self):
print('D')
class B(C,D):
pass
# def fuc(self):
# print('B')
b = B()
b.fuc() #C
第二次执行fuc
再注释掉C类的方法
class A:
def fuc(self):
print('A')
class C(A):
pass
# def fuc(self):
# print('C')
class D(A):
def fuc(self):
print('D')
class B(C,D):
pass
# def fuc(self):
# print('B')
b = B()
b.fuc() #D
第三次执行fuc
所以在最后一次执行的结果就是A了
我们也可以通过B.mro()的方法来知道python是怎么走的
class A:
def fuc(self):
print('A')
class C(A):
def fuc(self):
print('C')
class D(A):
def fuc(self):
print('D')
class B(C,D):
def fuc(self):
print('B')
b = B()
print(B.mro()) #[<class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
结果
在这里为什么先找的是D而不是A呢?虽然python在找的时候它其实已经知道了C后面有一个A,但是它要优先遵循从左往右的方向去找并且C->A,D->A,如果它直接找到A的话那么D的节点就会丢失,如果一个节点丢失的话就再也找不回来了,所以第三次结果它打印了D。

<2>乌龟继承:
这些类的继承关系如下图表示

class A:
def fuc(self):
print('A')
class B(A):
def fuc(self):
print('B')
class F(A):
def fuc(self):
print('F')
class C(B):
def fuc(self):
print('C')
class E(F):
def fuc(self):
print('E')
class D(E,C):
def fuc(self):
print('D')
print(D.mro())
#[<class '__main__.D'>, <class '__main__.E'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
这种记录继承顺序它是新式类的继承顺序,所遵循的是广度优先
而在python2.7中就是经典类,它所遵循的深度优先即走过的路就不走了,在这里的结果就是D->E->F->A->C->B
总结:
如果是多个父类中有一个方法的名字都相同,一个子类继承了这些父类,当它去用这个方法的时候,它会优先从左往右去找
python2.7 新式类和经典类共存,新式类要继承object
python3 只有新式类,默认继承object
经典类和新式类还有一个区别就是mro方法之在新式类存在
super的本质
用到上面钻石继承的继承关系图,但代码稍微改动
class A:
def fuc(self):
print('A')
class C(A):
def fuc(self):
super().fuc()
print('C')
class D(A):
def fuc(self):
super().fuc()
print('D')
class B(C,D):
def fuc(self):
super().fuc()
print('B')
b = B()
b.fuc()
# A
# D
# C
# B
super它的本质不是单纯找父类,而是根据调用者的节点位置的广度优先顺序来找的
具体执行流程:

Python学习日记(二十四) 继承的更多相关文章
- Python学习日记(三十四) Mysql数据库篇 二
外键(Foreign Key) 如果今天有一张表上面有很多职务的信息 我们可以通过使用外键的方式去将两张表产生关联 这样的好处能够节省空间,比方说你今天的职务名称很长,在一张表中就要重复的去写这个职务 ...
- Python学习日记(二十八) hashlib模块、configparse模块、logging模块
hashlib模块 主要提供字符加密算法功能,如md5.sha1.sha224.sha512.sha384等,这里的加密算法称为摘要算法.什么是摘要算法?它又称为哈希算法.散列算法,它通过一个函数把任 ...
- Python学习(二十四)—— 前端基础之Bookstrap
转载自:http://www.cnblogs.com/liwenzhou/p/8214637.html 一.Bootstrap介绍 Bootstrap是Twitter开源的基于HTML.CSS.Jav ...
- Python学习日记(二十五) 接口类、抽象类、多态
接口类 继承有两种用途:继承基类的方法,并且做出自己的改变或扩展(代码重用)和声明某个子类兼容于某基类,定义一个接口类interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子 ...
- Python学习日记(二十二) 初识面向对象
引子 假设我们要开发一个关于飞机大战的游戏,那么游戏的里面就会有两个角色,分别是属于玩家操控的战机和敌方的战机,并且两个战机都有不同的技能或攻击方式,现在我们用自己目前所学的去写出下面的这些代码: d ...
- Python学习札记(二十四) 函数式编程5 返回函数
参考:返回函数 NOTE 1.高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. eg.求和函数 #!/usr/bin/env python3 def calsums(*args): a ...
- Python学习日记(二十九) 网络编程
早期的计算机通信需要有一个中间件,A要给B传东西,A必须要把信息传给中间件,B再把从中间件中拿到信息 由于不同机器之间需要通信就产生了网络 软件开发的架构 1.C/S架构 服务器-客户机,即Clien ...
- Python学习日记(二十六) 封装和几个装饰器函数
封装 广义上的封装,它其实是一种面向对象的思想,它能够保护代码;狭义上的封装是面向对象三大特性之一,能把属性和方法都藏起来不让人看见 私有属性 私有属性表示方式即在一个属性名前加上两个双下划线 cla ...
- python学习(二十四) 字符串格式化
1: Test 1 a = 'city' b = 'country' print(" aaa %s bbb %s " % (a, b)) result: aaa city bbb ...
随机推荐
- 使用nodejs+http(s)+events+cheerio+iconv-lite爬取2717网站图片数据到本地文件夹
源代码如下: //(node:9240) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' ...
- docker本地化异常:/bin/sh: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
docker中经常设置不了 环境变量$LC_ALL, 导致报很多奇怪的编码错误: /bin/sh: warning: setlocale: LC_ALL: cannot change locale ...
- js 经常用于条件判断 大于等于0 的正整数
/^\d+(?=\.{,}\d+$|$)/.test() // true 转:https://www.jianshu.com/p/feef5e62dd67
- linux升级python到2.7版本
linux的python安装包默认版本是2.6.6,yum程序默认也是依赖这个版本的python包的,但是其他一些程序如nodejs,却要的是2.7版本,因此必须要考虑升级后与yum的兼容问题.两步走 ...
- HttpUtils(2)
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; impo ...
- layui table.reload的bug
bug1: bug描述:当cols列在reload中有变化时,渲染后部分cols列自动隐藏(并未对这些列设置hide:true) bug版本:2.3.5版本有此bug,今日更新最新版本2.5.5 仍有 ...
- stylelint那些事儿
一.参考文档 - http://stylelint.cn/ - https://stylelint.io/ - https://stylelint.io/user-guide/exampl ...
- Jenkins - 安装并启动Jenkins
1 - 关于Jenkins 构建流水线(build pipeline)工具Jenkins可以轻松地定义和管理各种各样的操作(构建.测试等),并将这些操作像管道pipe一样自由地进行组合,从而自动.流畅 ...
- Survey of single-target visual tracking methods based on online learning 翻译
基于在线学习的单目标跟踪算法调研 摘要 视觉跟踪在计算机视觉和机器人学领域是一个流行和有挑战的话题.由于多种场景下出现的目标外貌和复杂环境变量的改变,先进的跟踪框架就有必要采用在线学习的原理.本论文简 ...
- redis使用bit做只有两种情况的“状态“统计(如是否在线的用户统计)
1 记录在线用户数(活跃用户)? 比如redis中键a的value数据的二进制码是 0110 0110 0001 它总共有12位,在redis的位操作中,二进制中的第几位称为offset. 我们可以这 ...