继承和多态


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

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

>>> class Zhong(object):
def run(self):
print('I am iron man')

当我们需要编写DogCat类时,就可以直接从Zhong类继承:

>>> class Dog(Zhong):
pass >>> class Cat(Zhong):
pass

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

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

>>> xss = Dog()
>>> xss.run
<bound method Zhong.run of <__main__.Dog object at 0x051BEE30>>
>>> xss.run()
I am iron man

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

>>> class Dog(Zhong):
def eatme(slef):
print('eatme OK?')

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

>>> class Dog(Zhong):
def run(self):
print('i') >>> class Cat(Zhong):
def run(self):
print('i') >>> xss.run
<bound method Zhong.run of <__main__.Dog object at 0x051BEE30>>
>>> xss.run()
I am iron man
>>> XSS =Dog()
>>> XSS.run
<bound method Dog.run of <__main__.Dog object at 0x05916470>>
>>> XSS.run()
i
>>> CRSF = Cat()
>>> CRSF.run
<bound method Cat.run of <__main__.Cat object at 0x05916330>>
>>> CRSF.rum()
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
CRSF.rum()
AttributeError: 'Cat' object has no attribute 'rum'
>>> CRSF.run()
i

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

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

>>> a =list()
>>> b= Zhong()
>>> c =Dog()

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

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

看来abc确实对应着listZhongDog这3种类型。

但是等等,试试:

>>> isinstance(c,Zhong)
True

看来c不仅仅是Dogc还是Zhong

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

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

>>> isinstance(b,Zhong)
True
>>> isinstance(b,Dog)
False

Dog可以看成Zhong,但Zhong不可以看成Dog

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

>>> def run_twice(Zhong):
Zhong.run()
Zhong.run()

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

run_twice(Zhong())
I am iron man
I am iron man

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

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

>>> def run_twice(Zhong):
Zhong.run()
Zhong.run() >>> run_twice(Zhong())
I am iron man
I am iron man
>>> run_twice(Cat())
i
i
>>> run_twice(Dog())
i
i

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

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

'
>>> class xre(Zhong):
def run(self):
print('dada') >>> run_twice(xre())
dada
dada
>>>

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

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

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

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

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

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

静态语言 vs 动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

 class XT(object):
def run(self):
print('LE')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

小结

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

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

python学习之路(25)的更多相关文章

  1. Python学习之路-Day2-Python基础2

    Python学习之路第二天 学习内容: 1.模块初识 2.pyc是什么 3.python数据类型 4.数据运算 5.bytes/str之别 6.列表 7.元组 8.字典 9.字符串常用操作 1.模块初 ...

  2. python学习之路-day2-pyth基础2

    一.        模块初识 Python的强大之处在于他有非常丰富和强大的标准库和第三方库,第三方库存放位置:site-packages sys模块简介 导入模块 import sys 3 sys模 ...

  3. Python学习之路-Day2-Python基础3

    Python学习之路第三天 学习内容: 1.文件操作 2.字符转编码操作 3.函数介绍 4.递归 5.函数式编程 1.文件操作 打印到屏幕 最简单的输出方法是用print语句,你可以给它传递零个或多个 ...

  4. Python学习之路-Day1-Python基础

    学习python的过程: 在茫茫的编程语言中我选择了python,因为感觉python很强大,能用到很多领域.我自己也学过一些编程语言,比如:C,java,php,html,css等.但是我感觉自己都 ...

  5. python学习之路网络编程篇(第四篇)

    python学习之路网络编程篇(第四篇) 内容待补充

  6. Python学习之路【第一篇】-Python简介和基础入门

    1.Python简介 1.1 Python是什么 相信混迹IT界的很多朋友都知道,Python是近年来最火的一个热点,没有之一.从性质上来讲它和我们熟知的C.java.php等没有什么本质的区别,也是 ...

  7. python 学习之路开始了

    python 学习之路开始了.....记录点点滴滴....

  8. python学习之路,2018.8.9

    python学习之路,2018.8.9, 学习是一个长期坚持的过程,加油吧,少年!

  9. Python学习之路——pycharm的第一个项目

    Python学习之路——pycharm的第一个项目 简介: 上文中已经介绍如何安装Pycharm已经环境变量的配置.现在软件已经安装成功,现在就开始动手做第一个Python项目.第一个“Hello W ...

  10. python学习之路------你想要的都在这里了

    python学习之路------你想要的都在这里了 (根据自己的学习进度后期不断更新哟!!!) 一.python基础 1.python基础--python基本知识.七大数据类型等 2.python基础 ...

随机推荐

  1. [codeforces1234F]Yet Another Substring Reverse

    题目链接 大致题意为将某个子串进行翻转后,使得不包含相同字符的字符子串长度最长.只能翻转一次或零次. 设一个子串的状态为包含字符的二进制.如子串为$abacd$,则状态为$00000000000000 ...

  2. Typora---markdown

    一级标题 空格 编写内容 有序内容 +Tab 无序内容 -+Tab 代码块 print('hello world!') typora快捷键 标题1==ctrl +1 图片 表格 Ctrl + T 姓名 ...

  3. Codeforces 10D LCIS 求最长公共上升子序列及输出这个子序列 dp

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/qq574857122/article/details/34430283 题目链接:点击打开链接 题意 ...

  4. 07、poly-A内参和杂交内参(arrayanalysis的问题)

    为了验证杂交的质量,Affymetrix公司加入了两类嵌入探针组: 一.poly-A内参:包括lys.phe.thr.dap 对应的探针组名称为:AFFX-r2-Bs-lys-3_at.AFFX-r2 ...

  5. sql server 幂运算函数power(x,y)、square(x)、exp(x)

    --POWER(x,y)函数返回x的y次乘方的结果值 --SQUARE(x)函数返回指定浮点值x的平方 --EXP(x)函数返回e的x乘方后的值 示例:select POWER(2,2), POWER ...

  6. 点亮指路灯led

    为什么要使用LED? (bootloader,kernel)开发初期,由于串口等硬件尚未被初始化,因此调试手段相当有限,这时通常会采用LED来做为程序调试的重要手段. LED驱动设计: 1.设置GPI ...

  7. 一、Signalr WebApi客服-数据传输实体

    一.定义消息传输的格式 res不受自己控制 接受ret是自己处理,但是必须包含头像等一系列信息,所有发送的时候消息也是需要传头像的.

  8. h5 实现页面上拉加载更多数据

    您好,您的上拉加载更多的代码来喽: html:(style部分:html部分:js部分--js部分主要就是监控上拉,调接口) //上拉加载更多css <style> .wait-loadi ...

  9. pycharm 的一个小问题

    版本:PyCharm 2018.3.7 (Professional Edition) 这段时间用pycharm写python代码,运行网上copy的代码.报错了也就是少个模块或者Python2的语法在 ...

  10. MySQL 数据库的创建、修改、删除、跳转

    一. 创建数据库 []代表可选项. IF NOT EXISTS:在创建数据库之前进行判断,只有该数据库目前尚不存在时才能执行操作. <数据库名>:创建数据库的名称.MySQL 的数据存储区 ...