类的定义22回顾:对象对象的构成:–一组相关信息存储在实例变量中–处理该信息的一组方法对象内的函数对象的属性:实例变量和方法333回顾:对象类决定了对象具有哪些信息和方法–对象是类的实例–通过类的构造子创建新对象定义自己的类:即以OO方法来组织自己程序要处理的数据画圆Point5050x:y:Circlecirc:40center:radius:draw()move()circ=Circle(Point(50,50),40)555回顾:对象对象是一个类的实例–类是数据类型(int,float,string)的推广--型–实例/对象--值实例变量vs.变量方法vs.函数66编程实例:炮弹模拟程序规格–输入:发射角,初速度,初始高度–输出:射程注:(1)不用微积分,只用一些基本知识来算法化解决(2)不考虑风阻力,只考虑重力77编程实例:炮弹模拟算法输入模拟参数:角度,速度,高度,计算位置变化的时间间隔计算炮弹初始位置xpos,ypos计算炮弹初始水平和垂直速度xvel,yvel当炮弹还在飞行,循环:更新一个时间间隔之后的xpos,ypos,yvel输出xpos88编程实例:炮弹模拟(续)defmain():angle=input(...(indegrees))vel=input(“...(inmeters/sec))h0=input(...(inmeters))time=input(...(inseconds))xpos,ypos=0.0,h0theta=angel*math.pi/180.0xvel=vel*math.cos(theta)yvel=vel*math.sin(theta)whileypos=0.0:#该条件允许h0=0.0更新printDistance:%0.1fmeters.%(xpos)999编程实例:炮弹模拟(续)算法核心部分:更新各值xpos=xpos+xvel*timeyvel_new=yvel9.8*timeypos=ypos+time*(yvel+yvel_new)/2yvel=yvel_new–10个变量–可读性不够好101010编程实例:炮弹模拟(续)模块化设计:defmain():angle,vel,h0,time=getInput()xpos,ypos=0,h0xvel,yvel=getXYComponents(vel,angle)whileypos=0.0:xpos,ypos,yvel=updatePos(time,xpos,ypos,xvel,yvel)printDistance:%0.1fmeters.%(xpos)–变量theta和vyel_new呢?这是自顶向下逐步求精的SoC带来的好处111111编程实例:炮弹模拟(续)但updateData()似乎不太好?函数updateData()的弊端–过多参数:5个参数,3个返回值.–函数参量过多通常意味着有更好的组织方式121212编程实例:炮弹模拟(续)OO设计设计一个抛物体类Projectile.从而:defmain():angle,vel,h0,time=getInput()cball=Porjectile(angle,vel,h0)whilecball.getY()=0.0:cball.update(time)#把细节封装起来printDistance:%0.1fmeters.%(cball.getX())–隐藏了对炮弹的描述信息:xpos,ypos,xvel,yvel类的定义语法class类名:方法定义–方法定义:同函数定义方法是依附于类的函数.一般函数则是独立的方法的第一个参量是专用的–指向方法的作用对象–传统上习惯用self这个名字13141414例:类定义多面骰(tou)子(色子)#msdie.pyfromrandomimportrandrangeclassMSDie:def__init__(self,s):#self表示任意对象,代指未来的对象(C++,this)self.sides=sself.value=1defroll(self):self.value=randrange(1,self.sides+1)#+1?defgetValue(self):returnself.valuedefsetValue(self,v):#cheat!self.value=v类的定义语法class类名:方法定义–方法定义:同函数定义–回忆:对象是数据和操作的结合上面的类定义中,方法对应于操作.但数据呢?15实例变量Python的类并不明显定义实例变量,而是在方法中直接使用.–主要是在__init__方法中(见后)用self.实例变量的方式给出如MSDie中的sides和value16实例变量每个类的实例(对象)具有自己的实例变量副本,用来存储该对象自己的数据对实例变量的访问:对象.实例变量实例变量与函数局部变量不同!–实例变量是依附于实例的可用于其他方法或某个方法的再次调用–函数中的变量是局部的,函数结束即不存在17181818例:类定义多面骰子#msdie.pyfromrandomimportrandrangeclassMSDie:definit__(self,s):#self表示任意对象,代指未来的对象(C++,this)self.sides=sself.value=1x=5defroll(self):y=self.value#正确self.value=randrange(1,self.sides+1)z=x#错!defgetValue(self):returnself.valuedefsetValue(self,v):self.value=v方法调用方法调用:同函数调用,但需指明作用对象.–因此不需要再为形参self提供实参了例如:主程序main()中执行die1.setValue(8)19方法调用调用程序挂起(暂停),解释器在对象的类中找到对应的方法方法中的形参赋值为调用者的实参–self的实参就是对象名,不同于其他实参的给出方式执行方法体控制权返回调用者,执行下条指令构造器对象构造器(constructor)__init__用法:(1)在类外部用类名生成新实例:die1=MSDie(6)(2)Python创建一个MSDie新实例,并对该实例调用__init__(),从而初始化其实例变量:die1.sides=6die1.value=121编程实例:Porjectile类构造器–炮弹的实例变量:xpos,ypos,xvel,yvel–构造器需要三个初值来为实例变量初始化:cball=Projectile(angle,vel,h0)–因此得到:def__init__(self,a,v,h):self.xpos=0self.ypos=htheta=math.pi*a/180self.xvel=v*math.cos(theta)self.yvel=v*math.sin(theta)22编程实例:Porjectile类(续)读取实例变量的方法defgetX(self):returnself.xposdefgetY(self):returnself.ypos23编程实例:Porjectile类(续)更新实例变量的方法:defupdate(self,time):self.xpos=self.xpos+self.xvel*timeyvelnew=self.yvel–9.8*timeyvelavg=(self.yvel+yvelnew)/2self.ypos=self.ypos+yvelavg*timeself.yvel=yvelnewOO版炮弹模拟程序:cball3.py24封装封装(encapsulation):–如果确认某类对象对解决问题有用,即可当作此类对象已存在,从而直接使用对象功能–至于对象的实现细节则封装在相应类定义中黑箱子–这是抽象(忽略实现细节),也是SoC25封装Python的封装只是一种程序设计思想和方法,并非语言本身提供的能力–在程序的任何地方可以随意访问实例变量c.xpos–严格意义的OO内部细节在类外部是不可见的只能通过方法(接口)访问26272727类模块文件类定义可以单独构成模块,以提供给其他所有程序使用–就如同函数库一样–很多OO语言都提供类库282828类模块文件良好程序设计风格:使用文档注释(Documentation)来说明类功能和用法–Python提供专用注释:文档字符串(docstring)模块/类/函数下面的第一行可以是一个注释字符串.形如#projectile.pyThismoduleprovides...classProjectile:Thisclass...defgetX(self)Thisfunction...–docstring被系统保存在模块/类/函数的属性__doc__中,可访问printrandom.randrange.__doc__编程实例:掷骰子图形化模拟掷两个骰子,程序显示投掷结果.提供两个按钮:一个用来投掷,一个用来退出程序.显然需要两个GUI部件:按钮和骰子.定制GUI部件:按钮按钮:图形窗口中的矩形区域,点击可以影响应用程序的行为.应该支持的方法:–构造器–activate:按钮处于激活状态–deactivate:按钮处于失效状态–clicked:按钮被点击–getLabel:获得按钮标签应该存储的实例变量–可通过实现上述方法来明确需要哪些变量30按钮方法实现例:activate按钮激活时:–边框由细变粗–填充色由灰变黑defactivate(self):self.label.setFill(black)self.rect.setWidth(2)self.active=1–可见需要实例变量:Text对象label,Rectangle对象rect,布尔变量active–deactivate()的实现类似31按钮方法实现例:clicked按钮被点击:–getMouse()返回的坐标位于按钮矩形范围内defclicked(self,p):returnself.activeand\self.xmin=p.getX()=self.xmaxand\self.ymin=p.getY()=self.ymax–可见需要实例变量:xmin,xmax,ymin,ymax32按钮类:button.py#button.pyfromgraphicsimport*classButton:def__init__(self,win,center,width,height,label):初始化defclicked(self,p):......defgetLabel(self):......defactivate(self):......defdeactivate(self):......33定制GUI部件:骰子应该支持的方法:–构造器窗口中心位置尺寸–setValue:设置骰子投出的值预置所有可能的圆点,根据值来点亮相应的圆点34骰子类:DieView.py#dieview.pyfromgraphicsimport*classDieView:def__init__(self,win,center,size):初始化def__makePip(self,x,y):......辅助函数,外部不可见.(Python用下划线开头的名字表示私有成员)defsetValue(self,value):......35编程实例:掷骰子主程序#roller.pyfromrandomimportrandomfromgraphicsimportGraphWin,PointfrombuttonimportButtonfromdieviewimportDieViewdefmain():win=...画GUIpt=win.getMouse()whi