用实例解释Python中的继承和多态的概念


Posted in Python onApril 27, 2015

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:

class Animal(object):
  def run(self):
    print 'Animal is running...'

当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:

class Dog(Animal):
  pass

class Cat(Animal):
  pass

对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

dog = Dog()
dog.run()

cat = Cat()
cat.run()

运行结果如下:

Animal is running...
Animal is running...

当然,也可以对子类增加一些方法,比如Dog类:

class Dog(Animal):
  def run(self):
    print 'Dog is running...'
  def eat(self):
    print 'Eating meat...'

继承的第二个好处需要我们对代码做一点改进。你看到了,无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running...,符合逻辑的做法是分别显示Dog is running...和Cat is running...,因此,对Dog和Cat类改进如下:

class Dog(Animal):
  def run(self):
    print 'Dog is running...'

class Cat(Animal):
  def run(self):
    print 'Cat is running...'

再次运行,结果如下:

Dog is running...
Cat is running...

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样:

a = list() # a是list类型
b = Animal() # b是Animal类型
c = Dog() # c是Dog类型

判断一个变量是否是某个类型可以用isinstance()判断:

>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True

看来a、b、c确实对应着list、Animal、Dog这3种类型。

但是等等,试试:

>>> isinstance(c, Animal)
True

看来c不仅仅是Dog,c还是Animal!

不过仔细想想,这是有道理的,因为Dog是从Animal继承下来的,当我们创建了一个Dog的实例c时,我们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog本来就是Animal的一种!

所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:

>>> b = Animal()
>>> isinstance(b, Dog)
False

Dog可以看成Animal,但Animal不可以看成Dog。

要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个Animal类型的变量:

def run_twice(animal):
  animal.run()
  animal.run()

当我们传入Animal的实例时,run_twice()就打印出:

>>> run_twice(Animal())
Animal is running...
Animal is running...

当我们传入Dog的实例时,run_twice()就打印出:

>>> run_twice(Dog())
Dog is running...
Dog is running...

当我们传入Cat的实例时,run_twice()就打印出:

>>> run_twice(Cat())
Cat is running...
Cat is running...

看上去没啥意思,但是仔细想想,现在,如果我们再定义一个Tortoise类型,也从Animal派生:

class Tortoise(Animal):
  def run(self):
    print 'Tortoise is running slowly...'

当我们调用run_twice()时,传入Tortoise的实例:

>>> run_twice(Tortoise())
Tortoise is running slowly...
Tortoise is running slowly...

你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:

用实例解释Python中的继承和多态的概念

小结

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写;

有了继承,才能有多态。在调用类实例方法的时候,尽量把变量视作父类类型,这样,所有子类类型都可以正常被接收;

旧的方式定义Python类允许不从object类继承,但这种编程方式已经严重不推荐使用。任何时候,如果没有合适的类可以继承,就继承自object类。

Python 相关文章推荐
Python中pip更新和三方插件安装说明
Jul 08 Python
解决python打不开文件(文件不存在)的问题
Feb 18 Python
基于python-opencv3的图像显示和保存操作
Jun 27 Python
python通过txt文件批量安装依赖包的实现步骤
Aug 13 Python
浅谈Python3识别判断图片主要颜色并和颜色库进行对比的方法
Oct 25 Python
pytorch下大型数据集(大型图片)的导入方式
Jan 08 Python
opencv python在视屏上截图功能的实现
Mar 05 Python
在tensorflow以及keras安装目录查询操作(windows下)
Jun 19 Python
Python TestSuite生成测试报告过程解析
Jul 23 Python
Matplotlib.pyplot 三维绘图的实现示例
Jul 28 Python
Python中的面向接口编程示例详解
Jan 17 Python
Python中 range | np.arange | np.linspace三者的区别
Mar 22 Python
Python中设置变量访问权限的方法
Apr 27 #Python
理解Python中的类与实例
Apr 27 #Python
简述Python中的面向对象编程的概念
Apr 27 #Python
介绍Python中的__future__模块
Apr 27 #Python
在Python中使用第三方模块的教程
Apr 27 #Python
在Python中使用模块的教程
Apr 27 #Python
详细介绍Python中的偏函数
Apr 27 #Python
You might like
mysql建立外键
2006/11/25 PHP
在Mac OS上搭建PHP的Yii框架及相关测试环境
2016/02/14 PHP
PHP AjaxForm提交图片上传并显示图片源码
2016/11/29 PHP
javascript 密码强弱度检测万能插件
2009/02/25 Javascript
解析offsetHeight,clientHeight,scrollHeight之间的区别
2013/11/20 Javascript
Javascript基础知识(三)BOM,DOM总结
2014/09/29 Javascript
浅析javascript的return语句
2015/12/15 Javascript
jQuery焦点图轮播效果实现方法
2016/12/19 Javascript
JS 验证密码 不能为空,必须含有数字、字母、特殊字符,长度在8-12位
2017/06/21 Javascript
详解JavaScript按概率随机生成事件
2017/08/02 Javascript
JavaScript贪吃蛇小组件实例代码
2017/08/20 Javascript
iframe高度自适应及隐藏滚动条的实例详解
2017/09/29 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
2018/07/27 Javascript
JS删除String里某个字符的方法
2021/01/06 Javascript
layer设置maxWidth及maxHeight解决方案
2019/07/26 Javascript
JS实现长图上下滚动效果
2020/03/19 Javascript
[58:42]DOTA2上海特级锦标赛C组败者赛 Newbee VS Archon第一局
2016/02/27 DOTA
pandas表连接 索引上的合并方法
2018/06/08 Python
python事件驱动event实现详解
2018/11/21 Python
python爬虫之urllib,伪装,超时设置,异常处理的方法
2018/12/19 Python
pytorch 实现在预训练模型的 input上增减通道
2020/01/06 Python
Python中and和or如何使用
2020/05/28 Python
基于python实现简单C/S模式代码实例
2020/09/14 Python
使用Html5实现异步上传文件,支持跨域,带有上传进度条
2016/09/17 HTML / CSS
DKNY品牌官网:纽约大都会时尚风格
2016/10/20 全球购物
澳大利亚领先的优质葡萄酒拍卖会:Langton’s Fine Wines
2019/03/24 全球购物
法国滑雪假期的专家:Ski Planet
2019/11/02 全球购物
迷你西餐厅创业计划书范文
2013/12/31 职场文书
2014新年元旦活动策划方案
2014/02/18 职场文书
师范生自荐信模板
2014/05/28 职场文书
2014年文员工作总结
2014/11/18 职场文书
科技活动总结范文
2015/05/11 职场文书
党员公开承诺书(2016最新版)
2016/03/24 职场文书
致毕业季:你如何做好自己的职业生涯规划书?
2019/07/01 职场文书
暑假开始了,你的暑假学习计划写好了吗?
2019/07/04 职场文书
mybatis3中@SelectProvider传递参数方式
2021/08/04 Java/Android