Python中的多态及抽象类(python的多态和java多态)
off999 2024-10-25 13:43 53 浏览 0 评论
本书同名免费MOOC《Python编程基础及应用》在哔哩哔哩(B站)热播,作者带着你学。
版权声明:本文内容引用自作者的图书《Python编程基础及应用》(高等教育出版社)。本文可以在互联网上转载传播,但必须包含文中的版权声明;本文不可以以纸质出版为目的进行摘抄或改编。
9.8 多态、抽象类
多态(polymorphism)是面向对象程序设计的一个重要概念,源自希腊语,意即“有多种形态”。对于程序设计而言,大致可以理解为:即使你不知道变量指向哪种形态,也能够对其执行操作,而且操作的行为将随对象的类型不同而不同。
对于Python程序员而言,可以不关心这个概念。因为在Python语言中,默认就是多态的。也就是说,对象被执行某个函数的时候,他的行为跟你想的多半是一样的。但作者认为,读者还是应该了解这个概念并理解其在程序设计中的重要性。
想象一个Word类软件,WPS或者Open Office之类,在文档中,用户会加入非常多的界面元素,包括但不限于:三角形、箭头、段落、圆形、矩形、艺术字、图片。对,就跟你想的一样,在面向对象程序设计中,这些元素都会使用类来描述,并且,设计者一定会为这些界面元素提供一个统一的父类。作者把这个类结构简化成下图的模样,读者要明白真实的情况比这个要复杂得多,但基本结构大体如此。
可以看到,所有的界面元素,三角形、圆形、... 、 文本段落(paragraph),被描述成拥有一个共同的祖先类-Shape。这个祖先类可以是抽象类,这意味着系统不允许你创建Shape类的对象,具体的稍后描述。抽象类什么具体的工作也不做,只是描述了他的全部后代的模样:至少实现draw()-描绘自身以及getSize()-返回元素在页面中的空间尺寸信息这两个方法。这种描述是强制性的,它的后代必须实现这两个方法或函数。
Triangle类用三个点坐标来描述自己的属性,除了实现必须的draw()和getSize()方法外,还实现了一个getArea()方法以计算自身所占面积。
Circle类则用一个圆点坐标以及一个半径来描述自己,也实现了额外的getArea()函数。
文本段落(Paragraph)类则用一个字符串sContent来存储其文本内容,另外,还额外实现了setFont()函数来设置文本的字体和字体大小。
作者把这4个类组织一个程序文件里,命名Shapes.py。首先,我们定义抽象基类Shape。
#Shapes.py
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def getSize(self):
pass首先,我们从abc模块导入了ABC及abstractmethod。abc的意思作者猜应该是abstract base class - 抽象基类的首字母缩写。Shape被描述成ABC的子类,draw()函数以及getSize()函数都打上了@abstractmethod装饰符,这说明这两个函数为抽象函数。这种情况下,系统是不允许创建一个Shape对象的。这个抽象类存在的唯一意义就是规定了它的后代类的某些特征:必须实现 draw()及getSize()方法。
如果尝试实例化Shape, 比如s = Shape(),就会得到下述错误信息:
Traceback (most recent call last):
File "D:/pylearn/C9_OOP/Shapes.py", line 11, in <module>
s = Shape()
TypeError: Can't instantiate abstract class Shape with abstract methods draw, getSize当且仅当Shape的某个后代类定义实现了Shape的全部抽象函数,该类才可以被实例化 - 即允许创建该类型的对象。下面是Triangle类、Circle类、Paragraph类的极简版本代码,注意,连同Shape类,这4个类都在同一个py文件中。
#Shapes.py
...
class Triangle(Shape):
def __init__(self):
self.point0 = (0,0)
self.point1 = (0,0)
self.point2 = (0,0)
def draw(self):
print("Triangle::draw")
def getSize(self):
pass #detail omitted
def getArea(self):
return 0 #it should be w * h / 2
#Shapes.py
...
class Circle(Shape):
def __init__(self):
self.ptCenter = (0,0)
self.iRadius = 0
def draw(self):
print("Circle::draw")
def getSize(self):
pass
def getArea(self):
return 0
#Shapes.py
...
class Paragraph(Shape):
def __init__(self):
self.sContent = ""
def draw(self):
print("Paragraph::draw")
def getSize(self):
pass
def setFont(self,fontName,fontSize):
pass这三个类都实现了Shape抽象基类的全部抽象方法,都不再“抽象”,可以实例化。另外,我们还注意到这三个类的构造函数里都没有调用执行Shape类的构造函数,这里这样做是可以的,因为Shape类没有自定义构造函数,或者说Shape类的构造函数什么也没做。
让我们把关注点放在这三个类的draw()函数上。Office类软件会用文档来组织这些界面元素,在这里我们把这些界面元素想像成一个列表,在这个列表里包含了很多个三角形、矩形、段落、图片、艺术字、公式、图表对象,这些对象都是Shape类的子对象,都实现了draw()方法。
当一个页面被显示出来时,软件会遍历这个列表,然后逐一调用列表内Shape子对象的draw()方法,以便把每个界面元素画出来。对,你听得没错,就是每个对象自己画自己。因为三角形类了解三角形的数据表达形式,掌握描绘一个三角形的全部信息,由这个类的draw()来承担这个职责再合适不过了。圆形、段落这些类也是类似情况。我们设想一下,假设在页面上描绘三角形的任务不是由三角形类来完成,而是由外部代码来完成,那么外部代码就必须清楚并访问三角形对象内部的全部细节,如果这件事情真的发生的话,对于软件工程而言是灾难性的:外部代码知道太多关于三角形内部实现的细节! 内部实现的细节变成了接口的一部分! 三角形类接口不再简洁明了! 以后你如果想修改三角形的内部数据结构,这几乎不可能,因为外部代码也要跟着改,涉及的外部程序和修改点可能太多 --- 这种复杂的情况,我们称之为紧耦合 - tight coupling。而程序的松散耦合 - loose coupling,才是我们的目标。
#Shapes.py
...
t1 = Triangle()
t2 = Triangle()
c1 = Circle()
c2 = Circle()
p1 = Paragraph()
#doc模拟一个文档,将界面元素组织在列表中
doc = [p1,c2,t2,c1,t1]
#遍历全部界面元素,将它们全部画出来
def renderDocument(doc):
for x in doc:
x.draw()
renderDocument(doc)执行结果
Paragraph::draw
Circle::draw
Triangle::draw
Circle::draw
Triangle::draw在这段代码里,我们创建了两个三角形,两个圆形,一个段落 ,然后把它们放入一个列表中,这个列表就是一个文档的简化表达形式。renderDocument()函数遍历这个列表,逐一执行其元素的draw()方法。我们看到,renderDocument()函数并不清楚变量x的具体类型,它只认为x是一个Shape,实现了draw()方法,至于x到底是三角形、圆形或者别的什么界面元素,完全不关心。但是,我们发现,x是什么类型,就会执行什么类型的对应的draw()函数,并打印出对应的文字。这就是多态,这些变量类型未知,但自动展现出与类型对应的恰当行为。
作为初学者的你,看见这个觉得平淡无奇: 这不就是我想像的样子么! 是的,这正好说明Python语言的设计目标达到了,它几乎总是按你的设想来工作。但这样做是有代价的,代价之一就是执行效率低。在C++这种讲究效率的语言里,为了追求效率,多态不是默认的。
本节展示的类结构为程序的扩展提供了无限的可能及便利。如果Office软件试图建立一种全新的界面元素,比如某种直方图图表,软件设计者所要做的,就是继承Shape类并设计一个新的类,然后在新的类里实现全部抽象函数。然后,上述renderDocument()函数一个字符都不用修改,即可以拥抱新的界面元素的加入所带来的变化,以不变应万变!
相关推荐
- 安卓商店下载(安卓商店下载安装到手机)
-
要限制华为iPad上的下载,您可以按照以下步骤进行操作:首先,打开设置菜单,然后选择应用和通知。在应用和通知菜单中,您可以找到已安装的应用程序列表。选择您想要限制下载的应用程序,然后点击它。在应用程序...
- 电脑软件开机自启动怎么设置
-
1、首先找到自己华硕笔记本,然后把笔记本翻开,可以看到键盘的这一面,右上角有一个比较大的按钮(不同型号不一样),然后点击一下即可成功开机。2、点击开机按钮之后,可以看到笔记本的桌面出现了一个华硕log...
- 手机双系统怎么切换(oppo手机双系统怎么切换)
-
realmeQ3有系统分身功能打开设置,直接到搜索栏搜索系统分身并开启,就可以开启系统分身功能了,设置系统分身时会请用户另外设置一个不同于主系统的密码,锁屏后解锁时输入主系统密码进入主系统,输入分身...
- ie10浏览器官网入口(ie8_11浏览器官网)
-
在Windows10系统中,您可以按照以下步骤找到InternetExplorer(IE)浏览器:1.点击左下角的“开始”按钮。2.在弹出的菜单中,选择“设置”。3.在“系统”选项卡中,...
- win10安装版非ghost(u盘安装win10非ghost)
-
建议不要在DOS环境下进行分区,这样的技术其实已经很久没有更新了,比如盘符显示乱码等等等等。建议进入PE环境进行分区操作,这样在引导错误时能够有专门的工具修复。我在此强力建议不要使用GHOST系统,虽...
- win10关闭自动更新方法联想(联想笔记本关闭win10自动更新)
-
方法一鼠标单击左下角的开始菜单,选择设置;点击更新和安全;在默认界面,点击右侧的高级选项;将更新选项与更新通知下的所有选项关闭即可。方法二鼠标右键单击此电脑,选择管理;进入计算机管理界面,点击展开“服...
- 改wifi密码的网址(wifi改密码网站)
-
WiFi官方密码忘记了,用手机修改的话需要有相关的软件账号的登录才行,否则的话是无法登录进去进行密码的修改的,此外如果这个WiFi的话不是个人的WiFi应用,而是一个区域,或者说是平台的WiFi官方...
- 微星主板bios怎么进u盘装系统
-
一:首先将已经使用U启动软件制作好启动盘的U盘插入电脑主机后面的USB插口处,(注意:请不要用USB延长线来连接U盘,可能会造成无法读取U盘的情况!),然后开机启动计算机,待显示开机画面的时候连续按下...
- windows运行启动项命令(windows如何启动运行)
-
1、鼠标右击“windows”键可以在其中找到“运行”栏。2、通过“运行”栏可以弹出窗口,在窗口中输入要运行的程序,比如命令行输入“cmd”。3、通过“cmd”即可进入windows命令行,在其中可以...
- 万能wifi钥匙手机版(万能wifi钥匙手机版 哪种比较好用)
-
就是通过WiFi万能钥匙,可以不用密码,连接室外的共享WIFI。最基本功能就是WiFi共享。一般的WiFi密码可以用万能钥匙解开,用之前要打开数据流量获取一下地理位置。WiFi万能钥匙并不万能,别再相...
-
- 如何更改电脑屏幕亮度(更改电脑屏幕亮度win10)
-
系统:win71打开电脑,单击鼠标右键,出现菜单后,点击个性化2进入个性化,点击左下角的显示图标3进去显示页面,点击左侧的调整亮度4在下方有一个亮度调节条,鼠标左键按住调节条上面的框,向左拖,屏幕就会调暗,向右拖,屏幕就会调亮了。首先看一下...
-
2025-11-15 14:03 off999
- cad2025破解版安装(cad2020破解下载)
-
中望cad2024安装步骤如下:关闭杀毒软件。下载软件并解压缩。解压后进入文件夹AutoCAD2024内,选中文件AutoCAD_2024_Simplified_Chinese_Win_64bit_...
- 十大免费cad网站(十大免费cad网站免费观看有风险)
-
1、CAD看图王这是一款非常专业的手机看图、DWG画图、CAD测量制图为一体的手机综合性绘图看图神器,全球累计用户超过3500多万,值得推荐下载!2、迅捷CAD看图迅捷CAD看图支持天正全系列的手机C...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
