信息安全案例教程:技术与应用 第2版 课件 第7章 面向对象的程序设计_第1页
信息安全案例教程:技术与应用 第2版 课件 第7章 面向对象的程序设计_第2页
信息安全案例教程:技术与应用 第2版 课件 第7章 面向对象的程序设计_第3页
信息安全案例教程:技术与应用 第2版 课件 第7章 面向对象的程序设计_第4页
信息安全案例教程:技术与应用 第2版 课件 第7章 面向对象的程序设计_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第7章面向对象的程序设计信息学院2024引言7.1案例:模拟乒乓球比赛7.2类和对象实例7.3面向对象的基本特性7.4面向对象的程序设计过程7.5编程实践:tkinter中的弹出框7.6本章小结7.7习题在章首案例的指引下,本章将深入学习面向对象程序设计思想,其中发现并定义类是最基础也是最重要的。在编程实践中,还将学习如何用弹出对话窗口来处理输入和输出。7.1案例:模拟乒乓球比赛对象的概念更符合现实世界中的复杂情况,每个对象既有静态的属性,也有动态的行为。作为一名乒乓球赛选手,他(她)有自己的技能水平,可以在一场比赛中发球、得分。运行程序,出现消息提示框,对模拟情景进行简要介绍。点击“确定”后,出现获取输入的对话框。如果用户输入的数据非法,出现消息提示框,用户重新输入。也可以点击“Cancel”退出程序。输入0.6、0.5、10000,模拟结果显示在消息提示框里。7.1案例:模拟乒乓球比赛在面向对象程序设计中,程序被分解为若干个类,面向对象的程序设计过程就是发现并定义一系列类的过程。7.2类和对象实例7.2.1类的定义在面向对象程序设计中,我们需要根据问题来定义自己的类,而不仅仅是使用别人定义好的类。使用关键词class来定义类,其一般形式如下:class<class-name>():def__init__(self,<formal-parameters>):self.<attribute-name>=<value>…def<method-name>(self,<formal-parameters>):<statements>…7.2.1类的定义<class-name>就是类的名字,命名规则和变量、函数一样,习惯上为了区分,类名的首字母一般大写。类的定义里面包含若干方法的定义,方法的定义和函数的定义类似。不同的是方法的第一个形参都是self,即使没有任何其他形参,也需要有这个参数,它有着特殊的含义,指向对象自身。有了这个参数,在定义任何方法的时候都可以访问对象的属性(self.<attribute-name>)或者其他方法(self.<method-name>)。7.2.1类的定义类中定义的第一个方法是初始化方法__init__(),每次根据类创建对象实例的时候都会调用这个方法,通常用来对类所包含属性的初始化赋值。这些属性的值可以通过调用其他方法来进行改变或者获取,从而使得对象的属性在对象实例存续期间都是可见的,而不仅仅限于某个方法。类所包含的属性一般并不是直接可见的,可以通过调用方法来获取或修改属性的值。这种特性叫做封装性(encapsulation)。【例7-1】定义课程类Course在初始化方法中,定义属性self.courses,初始化为空列表,该列表将记录多门课程的名称、学分和成绩。定义一个从用户那里获取课程名称、学分和成绩的方法,命名为get_input()

,self.courses的值被修改。再定义一个输出课程信息的方法print_courses(),课程名称、学分和成绩之间用制表符分隔。【例7-1】定义课程类Course接下来定义几个用来进行数据统计的方法,分别是统计课程门数的total_courses()、统计总学分的total_credits()、计算GPA的gpa()。这几个方法中都访问了self.courses。在gpa()方法中还调用了另一个方法total_credits()。【例7-1】定义课程类Course将对CSV文件的访问独立出来,分别定义write_csv()方法和read_csv()方法来完成课程信息的写入和读取功能。无论是写入还是读取,都访问了属性self.courses。是否将课程信息存入CSV文件取决于是否调用这两个方法。7.2.1类的定义现实世界中的对象往往都可以定义成类,比如每个人都是一个对象,可以为所有人定义一个类。本章案例模拟现实世界中的乒乓球比赛,比赛选手也是对象,我们可以为选手定义一个类,命名为Player。首先考虑为了模拟比赛,选手类需要有哪些属性呢?基本属性就是他(她)的技能水平(self.prob),还有他(她)参加比赛时的得分(self.score)以及在一场比赛中他(她)已经发了多少次球(self.serve)。7.2.1类的定义fromrandomimportrandomclassPlayer:def__init__(self,prob):self.prob=prob#技能水平self.score=0#比赛得分self.serve=0#发球次数defwinServe(self):returnrandom()<=self.prob初始化方法为属性赋初值。除self之外,还有一个形参prob,初始化时将其赋值给对象的属性self.prob。定义winServe(),根据选手的技能水平来模拟在一次发球中能否赢得一分。7.2.1类的定义defincScore(self):self.score=self.score+1defincServe(self):self.serve=self.serve+1defgetScore(self):returnself.scoredefgetServe(self):returnself.serve定义incScore()和incServe()方法来对比赛得分和发球次数进行累加。定义getScore()和getServe()方法来返回某个时刻的比赛得分和发球次数。本章案例的初步版本形成,将程序文件保存为ch07.py,运行程序,如果有错误则进行修正。试一试7.2.2对象实例类只是一个模板,我们需要根据类来创建一个具体的对象,对这个对象进行操作,这个过程就是实例化。实例化过程其实就是调用类的初始化方法__init__()来构造一个对象,但与调用其他方法不同,我们使用类名而不是方法名来调用。【例7-2】在IDLE中创建Course类的实例从course模块引入Course类。用Course类名调用初始化方法来构造一个对象实例并赋值给变量stud_course,用这个变量来记录某个学生所学课程的名称、学分和成绩。调用get_input()方法来获取课程信息调用print_courses()方法查看课程信息调用total_courses()、total_credits()和gpa()调用write_csv()方法将课程信息存入CSV文件中【例7-2】在IDLE中创建Course类的实例重新启动Shell,再次创建Course类的对象实例并赋值给stud_course,此时调用print_courses()方法可以看到没有任何课程信息,因为这是一个新的被初始化的对象变量。调用get_input()方法获取课程信息调用write_csv()方法将课程信息存入文件调用total_courses()、total_credits()和gpa()调用read_csv()方法读取课程信息调用print_courses()方法,看到上一次和这一次输入的课程信息7.2.2对象实例本章案例中,我们已经定义好了选手类Player,下面在IDLE解释器中创建其对象实例并进行操作。从ch07模块引入Player类。>>>fromch07importPlayer然后用Player类名调用初始化方法来构造一个对象实例并赋值给变量playerA,其技能水平为0.6。>>>playerA=Player(0.6)比赛刚开始时playerA先发球,调用incServe()方法增加一次发球。>>>playerA.incServe()7.2.2对象实例调用winServe()方法来模拟一次playerA发球时能否赢得一分,结果为False,不得分。>>>playerA.winServe()False接下来仍然是playerA发球,调用incServe()方法再增加一次发球,。>>>playerA.incServe()7.2.2对象实例调用winServe()方法再模拟一次,结果为True,调用incScore()方法增加一分。>>>playerA.winServe()True>>>playerA.incScore()最后调用getServe()和getScore()方法观察当前playerA的发球次数和得分情况,分别为2和1。>>>playerA.getServe()2>>>playerA.getScore()1一场乒乓球比赛需要两位选手,再创建另一个选手对象playerB,继续至少两个回合的比赛,观察双方的发球次数和得分情况。试一试面向对象的基本特征包括封装性、继承(inheritance)和多态性(polymorphism),其中多态性和继承密切相关。7.3面向对象的基本特性7.3.1封装性封装把“what”和“how”区分开,在使用对象时我们只需要知道它是什么(即“what”),并不需要知道它是怎么实现的(即“how”)。当我们使用别人定义的类或函数的时候,很容易理解什么是封装性,因为我们并不知道这些类或函数的实现细节。在使用自己定义的类或函数的时候,我们既知道它是什么,也知道它是怎么实现的,但是仍然要区分开实现和使用。7.3.1封装性将实现和使用分离,只要外部接口不变,内部实现细节如何变化也不会影响外部的使用。封装性的另一个好处就是支持代码重用(codereuse),我们在一个模块里定义的类,可以很方便地在其他模块中引入,而无需重复写一遍代码,对类的定义修改了,所有引入它的模块都修改了。对象之间的交互就是消息的传递。调用对象的方法就是给这个对象发送一条消息,请求它按照这个方法的功能执行相应的操作。【例7-3】定义Person类记录人的ID、姓名、出生地等信息,并定义一个用来进行自我介绍的方法self_intro()

。一般做自我介绍时,不会介绍ID,所以单独定义一个返回ID的get_id()方法。创建Person类的对象实例并赋值给变量personA,初始化过程传递了相应的属性值作为参数,然后给personA发送一条消息,请他(她)做自我介绍。7.3.1封装性这个例子中可以这样理解封装性,在main()函数中并不知道Person类是如何定义的,也不知道它有哪些属性,但知道要用什么参数来构造一个对象实例(初始化方法的外部接口),也知道有一个self_intro()方法可供调用,但并不知道这个方法是如何进行自我介绍的。对self_intro()方法内部实现细节的修改,并不影响main()函数对它的调用。7.3.2继承和多态性在定义一个类时,可以从已有的类中继承其属性和方法,这个新定义的类称为子类(subclass),被继承的类称为父类(parentclass)或超类(superclass)。利用继承关系,子类无需重复定义父类中已有的属性和方法,也有利于代码重用。定义继承关系的形式如下:class<sub-class-name>(<parent-class-name>):我们可以基于Person类来定义Player类,使得Player类能够继承Person类中定义的所有属性和方法。7.3.2继承和多态性frompersonimportPersonclassPlayer(Person):def__init__(self,iden,name,birth,prob):Person.__init__(self,iden,name,birth)self.prob=prob#技能水平self.score=0#比赛得分self.serve=0#发球次数…defmain():

playerA=Player('01','ZhangSan','Beijing',0.6)

playerA.self_intro()if__name__=="__main__":main()在括号内加上父类的名字Person,就可以继承来自于Person类的属性和方法了。初始化一个选手对象时也需要传递Person类的属性值作为参数。首先调用父类的初始化方法,并将参数传递给它。创建一个选手对象时,除技能水平外,也传递了ID、姓名、出生地的参数值。7.3.2继承和多态性Player类中并没有重复定义Person类中的属性和方法,而是通过继承自动获得,提高了效率,结构上也更加层次分明,易于理解。上例中子类的对象直接继承了父类中定义的方法并调用了它,实际上子类还可以对父类中定义的方法进行重写,以满足自身特殊化需求。如果一个父类有多个子类,这些子类可以对从父类继承来的同一个方法进行重写,也就是同一个方法在父类和不同的子类中实现代码不同,这种特性就称为多态性。【例7-4】定义学生类Student定义一个获取学号的方法get_stu_id()。重写父类中定义的self_intro()方法,除了基本信息介绍外,还增加了学生信息的介绍。创建一个学生对象stud,传递基本信息和学生信息,调用子类中重写的self_intro()方法,调用父类中的get_id()方法和子类中的get_stu_id()方法。定义Person类的另一个子类:教师类Teacher,用来记录教师的工号、学校、学院等信息,重写Person类的self_intro()方法,使其包含教师信息。试一试面向对象的程序设计过程就是发现并定义一系列类的过程。7.4面向对象的设计过程序号步骤说明(1)寻找候选对象从问题描述开始仔细分析,对象通常是名词,且具有较为复杂的属性和行为(2)识别属性识别对象所包含的信息,一些属性可以用简单的数据类型表示,另一些则可能指向复杂的对象/类(3)设计接口对象类需要哪些操作,问题描述中有哪些描述行为的动词,列出类所需要的方法(4)设计重要方法采用自顶向下、逐步求精的方法对较为复杂的方法进行详细设计,进一步分解(5)不断迭代设计过程并非是线性的,设计新类、给现有类添加属性和方法都是不断迭代的(6)尝试替代方案好的设计都是不断试错(tryanderror),不要害怕放弃不可行的方案,也不要害怕尝试新的想法7.4.1寻找候选对象本章案例的问题描述是:模拟两位选手之间的很多场比赛,看看最终各胜负多少场。不难发现,“比赛”、“选手”都是关键的名词,将它们作为对象进行设计。同时,要模拟很多场比赛,并对结果进行统计,这个动作是“比赛”和“选手”无法完成的,因此,将“统计结果”也作为对象进行设计。解决问题的思路是:两位有着各自技能水平的选手打比赛,每打完一场比赛,就更新统计结果,打完N场比赛后输出最终统计结果。7.4.2设计并定义类选手类Player我们已经设计并定义好了,其中所有方法的设计和实现代码都非常简单。下面来设计并定义比赛类Game和统计结果类SimStats,需要说明的是,设计过程也是不断迭代和不断试错的,这里呈现的是最终设计结果。Game类“比赛”有哪些属性呢?首先需要两位选手,其次是每一个回合发球方是谁,根据乒乓球计分规则,发球方输球,对手方要得分。classGame:def__init__(self,probA,probB):#初始化一场比赛self.playerA=Player(probA)self.playerB=Player(probB)self.server=self.playerA

self.opponent=self.playerB构造一个Game对象需要传递的参数就是双方选手的技能水平probA和probB。self.playerA和self.playerB都是Player对象,初始化时传递的参数是技能水平。假设总是self.playerA先发球,那么对手方就是self.playerB。Game类“比赛”中最重要的操作就是“打”了,定义play()方法,采用自顶向下的方法进行进一步分解。defplay(self):whilenotself.isOver():self.checkServer()self.server.incServe()ifself.server.winServe():self.server.incScore()else:self.opponent.incScore()“打”的过程需要循环,终止条件是比赛结束,分解出isOver()方法来进行判断。每个回合都需要确定谁发球,分解出checkServer()方法来确定。发球方的发球次数累加一次,如果赢了则得一分,否则对手方得一分。Game类defisOver(self):

a,b=self.getScores()returnabs(a-b)>=2and(a>=11orb>=11)defgetScores(self):returnself.playerA.getScore(),self.playerB.getScore()根据双方选手的得分来判断比赛是否结束,设计并调用Game类的getScores()方法来获取双方选手的得分。分别调用两位选手的getScore()方法。Game类defcheckServer(self):

a,b=self.getScores()ifabs(a-b)<2and(a>10orb>10):#如果是10平之后self.changeServer()elifself.server.getServe()%2==0andself.server.getServe()!=0:self.changeServer()defchangeServer(self):ifself.server==self.playerA:self.server=self.playerBself.opponent=self.playerAelse:self.server=self.playerAself.opponent=self.playerB如果是10平之后,每发一个球都换发,否则发球方累计发球次数为偶数且不为0时换发。分解出changeServer()方法来完成换发球。Simstats类“统计结果”的属性就是双方选手各赢了多少场。classSimStats:def__init__(self):self.winsA=0self.winsB=0Simstats类“统计结果”中最重要的操作就是“更新”了,定义update()方法,根据一场比赛的结果来更新统计结果。defupdate(self,aGame):

a,b=aGame.getScores()ifa>b:#选手A赢了self.winsA=self.winsA+1else:#选手B赢了self.winsB=self.winsB+1defgetResult(self):returnself.winsA,self.winsB形参aGame是一个Game对象。调用其getScores()方法获取一场比赛结束后双方的得分。定义一个返回统计结果的方法getResult()。simpledialog和messagebox是tkinter中的两个包,前者用来弹出输入对话框,后者用来弹出消息对话框。fromtkinterimportsimpledialog,messageboxsimpledialog包的常用函数:7.5编程实践:弹出对话框函数功能askfloat(title=None,prompt=None)从用户那里获取一个小数,若不是,提示“非法值”,要求用户重新输入askinteger(title=None,prompt=None)从用户那里获取一个整数,若不是,提示“非法值”,要求用户重新输入askstring(title=None,prompt=None)从用户那里获取一个字符串,若不是,提示“非法值”,要求用户重新输入7.5编程实践:弹出对话框函数功能showerror(title=None,message=None)打开一个错误提示对话框showinfo(title=None,message=None)打开一个信息提示对话框showwarning(title=None,message=None)打开一个警告提示对话框askyesno(title=None,message=None)打开一个“是/否”对话框,返回True或Falseaskretrycancel(title=None,message=None)打开一个“重试/取消”对话框,返回True或Falsemessagebox包的常用函数:【例7-5】修改Course类定义的输入输出分别调用simpledialog中的askstring()和askinteger()函数接收课程信息输入。询问用户是否继续输入调用messagebox中的askyesno()函数。将所有要输出的字符串合并在变量info中,然后将其作为参数调用messagebox中的show

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论