版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章类Python程序设计基础教程(慕课版)工业和信息化人才培养规划教材国家精品资源共享课程配套教材高职高专计算机系列人民邮电出版社我们已经接触过多种数据类型:数字、字符串、元组、列表、字典等。它们可以用来描述不同的实际需要,适用不同的场合,解决不同的问题。本章将介绍另一种数据类型:类(class)。本章导读Thechapter’sintroduction目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.1.1类与对象的概念例如,一说到球,人的头脑中马上就会把它想象成一种圆形的物体,它的大小由半径决定,它的外表可以有不同颜色;对球施以外力后,它会往远处滚动,或上下来回弹跳。但是,我们能真正拿出一个叫“球”的物体吗?不可能,世上没有一个东西具体叫作“球”,只有诸如“乒乓球”“篮球”“排球”等,才是实实在在的“球”。因此,“球”与“乒乓球”等不是一回事。“球”,是对诸如“乒乓球”“篮球”“排球”等的一种抽象;而“乒乓球”“篮球”“排球”等,则是对“球”的一个个具体化。“球”是圆的,大小由半径决定,外表可以有不同颜色,施加外力后会滚动或弹跳,这些抽象的特征和行为,都是从“乒乓球”“篮球”“排球”等里面抽取出来的;而每一种具体的球,又都必须具有抽取出来的这些特征和行为。于是,人们把抽象的“球”视为是世间的一种数据类型——“类”;把具体的“乒乓球”“篮球”“排球”等,视为这种类的“实例化”,或者说是该类的一个个“对象”。6.1.1类与对象的概念可以举出很多类和对象的例子。例如,“动物”是类,“狮子”“羚羊”“狗”等是动物类的对象。但从另一个角度讲,“狗”是一个类,“猎狗”“宠物狗”“藏獒”等又是狗这类的对象。“宠物狗”又可以再细化成一个类,“京巴狗”“约克夏”“哈士奇”等是宠物狗类的对象。人们就是以这种“分门别类”的办法,一点点深入,一点点细化,逐渐对世间万物进行了解、描述和研究。从上面的讨论可以得出下面的两点共识。“类”是一种抽象的概念,它代表了现实世界中某些事物的共有特征和行为。“对象”是对类的实例化,每个对象都会自动具有所属类的通用特征和行为。当然,根据需要,对象也可以具有自己独特的个性特征与行为。目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.1.2Python中类的定义Python中的类是借助关键字class来定义的。具体语法如下:它由两大部分组成,以class开头、“:”冒号结束的第1行被称为“类头”,缩进的<成员变量>与<成员函数>被称为“类体”。其中,<类名>必须符合Python对变量取名所做的规定。进一步地,为了便于区分,Python约定<类名>的第1个字母必须大写。<成员变量>就是类的成员属性,简称“属性”。<成员变量>包含在描述该类成员(也就是对象)共有的抽象特征时涉及的各种变量。class<类名>():
<成员变量> <成员函数>6.1.2Python中类的定义下面直接举出3个Python中类的定义的例子,只要了解了前面关于函数的那些知识,那么大致理解例子中所要描述的内容便不会有太大的难度。<成员函数>即成员方法,简称“方法”。<成员函数>包含描述该类成员(也就是对象)所具有的抽象行为或动作的各种函数。在一个“类”的定义里,<成员变量>和<成员函数>当然可以不止一个。classDog():#定义类Dog def__init__(self,name,age): =name self.age=age defsit(self): print(.title()+'isnowsitting.') defr_over(self): print(.title()+'rolledover!')例6-1定义一个“狗”类:6.1.2Python中类的定义该类的名字是Dog,它的首字母为大写,符合Python对类的定义所做的约定要求。在缩进的类体里面,包含3个以def开头的函数。第1个是名为__init__的特殊方法,它是“初始化程序”。在创建对象时,用来为代表属性的诸变量赋初值。例如,类Dog里有3个形参,即self、name、age,除了特殊的self后面会专门讲述它的作用外,name和age两个属性的初值都会由创建对象(即实例化)语句提供的实参传递过来。第2个是名为sit(坐)的函数,它是描述狗会“坐”的这种行为的方法。该方法只有一个特殊参数self,功能是把狗名的第1个字母改为大写(由调用函数title()来实现),并输出“isnowsitting”信息。如果在此能够插入一些动画,那么就应该出现一条狗蹲坐在那里、舌头吐在嘴外面呼呼喘气的情景。第3个是名为r_over(奔跑)的函数,它是描述狗会“跑”的这种行为的方法。该方法只有一个特殊参数self,功能是把狗名的第1个字母改为大写(由调用函数title()来实现),并输出“rolledover!”信息。如果这里能够插入动画,那么就应该出现一条狗向前奔跑的情景。6.1.2Python中类的定义classEmployee(): #定义类Employeee_count=0
def__init__(self,name,salary): #初始化程序
=nameself.salary=salaryEmployee.e_count+=1
defdis_employee(self): #输出雇员信息
print('Name:',,',Salary:',self.salary)例6-2定义一个“雇员”类:该类的名字是Employee,它的首字母为大写,符合Python的约定要求。在缩进的类体里,先是为一个变量e_count赋初值0,由于它在类体的最前面,所以这个变量在整个类的定义里都有效,也就是类里定义的函数都可以使用它。6.1.2Python中类的定义该类里,第1个是名为__init__的初始化程序,我们后面会对它进行专门的讲述。除去特殊参数self外,它的两个属性name、salary的初始值,仍然是在创建对象(即实例化)时,靠创建语句通过实参传递过来。在这个方法里,还对变量e_count进行计数操作。该类体里的第2个函数是dis_employee(),只有一个特殊参数self,功能是输出雇员的名单。importmath #导入标准函数库mathclassCircle(): #定义类Circle def__init__(self,radius=14): self.radius=radius defd_diameter(self): #返回直径的方法
return2*self.radius defc_perimeter(self): #返回圆周长的方法
return2*self.radius*math.pi defr_area(self): #返回圆面积的方法
returnself.radius*self.radius*math.pi例6-3定义一个关于“圆”类:6.1.2Python中类的定义该类的名字是Circle,它的首字母为大写,符合Python的约定要求。注意,在整个类定义的外面,有一条导入语句“importmath”,表明该类里要用到有关数学计算的标准函数库。缩进的类体里有4个函数:第1个仍然是名为__init__的初始化程序,它除了特殊参数self外,还有一个名为radius(半径)的默认参数,取默认值为14。第2个方法名为d_diameter,只有一个特殊参数self,功能是返回圆的直径;第3个方法名为c_perimeter,只有一个特殊参数self,功能是返回圆周长;第4个方法名为r_area,只有一个特殊参数self,功能是返回圆面积。6.1.2Python中类的定义比较以上给出的3个类的定义,可以得到下面这样的一些对类的认识:当使用前面那些单一数据类型无法描述出世间的事物时,就应该考虑采用“类”这种数据类型;类名字的第1个字母,应该遵循大写的规则;类定义中,可以有一个名为__init__的初始化程序(它实际上也是一个方法),在它的里面聚集了抽象出来的属性;类中的各种方法里,即使是初始化程序,都有一个特殊的参数self,它总是被放在方法形参表的第1个位置处,哪怕方法里没有任何别的参数,这个self都是不可或缺的。综上所述,Python的类的定义,是把解决问题时需要用到的变量(即属性)和函数(即方法)组合在了定义中。通常,称这种组合为“封装”,前面的那些数据类型,都不可能实现这种把变量和方法封装在一起的效果。01OPTION02OPTION03OPTION04OPTION目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.1.3对象:类的实例化定义了类,就可以进行类的实例化工作了。也就是说,可以从“抽象”转为“具体”,创建出一个个现实世界中的对象来。创建的对象可以通过“对象名.成员”的方式,访问类中所列的<成员变量>和<成员函数>。例6-2中有关雇员的实例化。例如在类Employee定义的基础上,编写程序主体如下:emp1=Employee('Zara',2000) #创建一个名为emp1的对象emp2=Employee('Manni',5000) #创建一个名为emp2的对象emp1.dis_employee() #emp1调用方法dis_employee()emp2.dis_employee() #emp2调用方法dis_employee()print('TotalEmployee%d'%Employee.e_count) #输出雇员数6.1.3对象:类的实例化该程序有3部分内容。先是通过类Employee创建两个对象,即emp1和emp2,创建过程与调用函数类似,把实参“'Zara',2000”和“'Manni',5000”分别传递给类里的形参name和salary。注意,前面一再提及的特殊参数self没有出现,没有传递。其次是创建完对象后,采用句号分隔的办法,用不同的对象名,两次调用类中的方法dis_employee(),目的是利用该方法输出该雇员的信息。注意,类中方法里的self参数仍然没有出现。第3个内容是一条print语句,输出雇员数e_count。把类定义及程序主体合并在一起,然后保存并投入运行,结果如图6-1所示。这就是一个简单的面向对象的程序设计过程:先定义一个类,把有关的属性和方法封装在一起;然后创建对象,完成实例化;最后是程序的执行,获得所需要的结果。图6-16.1.3对象:类的实例化classEmployee():e_count=0def__init__(self,name,salary):=nameself.salary=salaryEmployee.e_count+=1defdis_employee(self):print('Name:',,',Salary:',self.salary)emp1=Employee('Zara',2000) #创建一个名为emp1的对象emp2=Employee('Manni',5000) #创建一个名为emp2的对象emp1.dis_employee() #emp1调用方法dis_employee()emp2.dis_employee() #emp2调用方法dis_employee()print('TotalEmployee%d'%Employee.e_count) #输出雇员数程序编写如下:6.1.3对象:类的实例化firstA=Circle(17) #创建一个名为firstA的对象firstB=Circle() #创建一个名为firstB的对象print('InformationonfirstA:')print('Radiusofthegarden',firstA.radius)print('Diameterofthegarden:',firstA.d_diameter())print('Gardenarea:',firstA.r_area())print('Gardenperimeter:',firstA.c_perimeter())print('\nInformationonfirstB:')print('Radiusofthegarden',firstB.radius)print('Diameterofthegarden:',firstB.d_diameter())print('Gardenarea:',firstB.r_area())print('Gardenperimeter:',firstB.c_perimeter())再来看例6-3里定义的“圆”类,编写程序主体如下:6.1.3对象:类的实例化程序开始处,通过类Circle创建了两个对象,一个名为firstA,一个名为firsrB。在类Circle的初始化程序里,除了特殊参数self外,有一个默认参数radius,创建firstA对象时,没有使用这个默认参数,而是把自己的参数值17传递了过去;创建firstB对象时,使用了默认参数,所以无须传递任何参数给类的初始化程序。这些用法完全与函数相同。紧接着是分两组输出语句输出两个实例化的圆的半径、圆的直径、圆的面积、圆的周长。把类定义及程序主体合并在一起,然后保存并执行,结果如图6-2所示。图6-26.1.3对象:类的实例化importmathclassCircle(): def__init__(self,radius=14): self.radius=radius defd_diameter(self): return2*self.radius defc_perimeter(self): return2*self.radius*math.pi defr_area(self): returnself.radius*self.radius*math.pi程序编写如下:firstA=Circle(17)firstB=Circle()print('InformationonfirstA:')print('Radiusofthegarden',firstA.radius)print('Diameterofthegarden:',firstA.d_diameter())print('Gardenarea:',firstA.r_area())print('Gardenperimeter:',firstA.c_perimeter())print('\nInformationonfirstB:')print('Radiusofthegarden',firstB.radius)print('Diameterofthegarden:',firstB.d_diameter())print('Gardenarea:',firstB.r_area())print('Gardenperimeter:',firstB.c_perimeter())目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.2.1关于初始化程序:__init__下面罗列几个编写初始化程序时需要了解的问题。#下面是定义的函数,它有了返回值defodd_even(x): if(x%2==0): result=True else: result=False returnresult #return必须保证在函数体的缩进范围内#下面是调用函数的程序print('Pleaseenteraninteger!')num=input('Enteraninteger:')y=int(num)#调用函数odd_even(),变量z接收函数的返回值z=odd_even(y) ifz==True: print('It\'sanevennumber!')else: print('It\'sanoddnumber!')print('End')6.2.1关于初始化程序:__init__下面罗列几个编写初始化程序时需要了解的问题。1.初始化程序的正确写法:两条下划线初始化程序的名字,是以两条下划线开始,以两条下划线结束的。如果哪一边只有一条下划线,那么就会输出图6-3所示的出错信息,表明创建对象时,实参无法传递给类,导致对象得不到参数。这种错误提示得非常隐晦,让人难以发现到底是在哪里出了问题,其实只是因为丢失了一条小小的下划线“—”。2.初始化程序,不一定非要排在类定义的最前面初始化程序的名字是固定的,只能写成“__init__”。因此,只要保证它出现在类定义语句块的里面就行,至于是块中的第1个方法(如例6.1~6.3那样),还是第几个方法,完全无关紧要;甚至如果不需要传递什么参数,在类的定义中不编写初始化程序也没有什么关系。Python在创建一个新的对象(即实例化)时,总是用这个名字去匹配类中所包含的方法名,找到这个名字,就执行并仅执行它一次,以完成初始化的工作;如果没有找到它,那就不做初始化工作。6.2.1关于初始化程序:__init__3.初始化程序中name与的不同含义例如在例6-2的类定义中,初始化程序如下:def__init__(self,name,salary):=nameself.salary=salaryEmployee.e_count+=1在它的里面除self外,还有两个位置参数:name、salary。一进入初始化程序,就执行:=nameself.salary=salary把接收到的实参(在name和salary里),赋予变量和self.salary。因此,这两条语句右边的name和salary,是初始化程序接收到的传递过来的实参,而左边的与self.salary是两个变量,由它们接收并存放这个新建对象的具体属性值。6.2.1关于初始化程序:__init__既然左边的与self.salary是两个变量名称,因此除了前面的self不能变以外,句号分隔的name与salary完全可以另取名字,例如改名为self.ad和self.bc,这也是完全可以接受的。人们之所以喜欢将它们取名为与self.salary,主要是因为name、salary是类的属性名,有它们就知道这个变量里面存放的是什么属性值。例如,把前面给出的类Dog里面的:=nameself.age=age改为:self.ad=nameself.bc=age6.2.1关于初始化程序:__init__classDog(): def__init__(self,name,age): self.ad=name #变量名变了
self.bc=age #变量名变了
defsit(self): print(self.ad.title()+'isnowsitting.') #这里要做相应改动
defr_over(self): print(self.ad.title()+'rolledover!') #这里要做相应改动mdog=Dog('willams',6)print('Mydog\'snameis'+str(mdog.ad.title())+'.') #这里要做相应改动print('Mydogis'+str(mdog.bc)+'yearold!') #这里要做相应改动整个类和程序如下所示:运行该程序,完全可以得到正确的结果。不过要注意的是,如果把类中接收实参值的变量名改了,那么程序中相应的地方也都要改动,否则就会出错。目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.2.2关于参数:self例6-4下面是一个用类编写的极为简单的图书查询的例子(为方便讲述,各部分程序分散列在下面):#定义一个字典catalogcatalog={'aa':3,'bb':0,'cc':5,'dd':2,'ee':4,'ff':1,'gg':8,'hh':3}#定义类BookclassBook(): globalcatalog def__init__(self,name): =name #下面是类中的方法loop(),供创建的对象调用
defloop(self,name): forkey,valueincatalog.items(): ifkey==name: ifvalue!=0: print('Thereisthebook!') elifvalue==0: print('I\'msorrythatthebookhasbeenborrowed!') else: print('It\'sapitythatthereisnobook!') break6.2.2关于参数:self#下面是类中的方法seek(),供创建的对象调用defseek(self,pword,name,value): ifpword!='12345': returnprint('Youdon\'thavetherighttoaddanewbook!') else: catalog[name]=value print(catalog)整个程序开始,先定义一个字典catalog,书名aa、bb、cc等是字典的键;现存书的数量是字典的值。由于字典catalog是在整个程序外定义的,进入类后才使用它,这样可能会引起不必要的误解,所以在类的里面用语句globalcatalog表明类里出现的catalog就是外面定义的全局变量catalog。整个类Book里有3个函数。6.2.2关于参数:self第1个是初始化程序__init__,它有两个参数,一个是self,一个是name。实例化时,name将接收传递过来的书名,并将其赋予变量。第2个方法名为loop,它有两个参数,一个是self,一个是是希望查找的书名,通过对字典catalog的遍历,给出查找的结果。在查找过程中,考虑这样几个问题:找到,且当前有这本书(数量不为0);找到,但当前没有这本书可外借(数量为0);没有找到这本书,查找失败。不同的情况会输出不同的信息:找到且当前有此书,输出信息“Thereisthebook!”;找到但当前没有这本书,输出信息“I'msorrythatthebookhasbeenborrowed!”;没有找到所需要的书,则输出信息“It'sapitythatthereisnobook!”。第3个方法名为seek,它有4个参数,self、pword、name、value。后面3个参数,一个是口令(pword)、一个是要添加的书名(name)、一个是数量(value)。只有在调用方法loop()查找没有结果时,才有可能往字典里添加新书;只有在输入的口令正确时,才能往字典里添加新书。也就是说,不是随便什么人都可以往字典里添加新书的。6.2.2关于参数:self程序主体分为两段:一个是创建类Book的对象studentA,在输入书名后,调用类的方法loop(),以便查找是否有所需的书目;另一个是创建类Book的对象studentB,在输入书名后,调用类的方法loop(),只有在调用方法loop()无结果的情况下,才去输入口令、数量,才去调用方法seek(),才有可能把新书添加到字典中去。程序编写如下:#下面是程序中创建的对象studentAbk=input('Enterthetitleoftherequiredbook:')studentA=Book(bk)studentA.loop(bk)#下面是程序中创建的对象studentBbnm=input('Enterbookname:')studentB=Book(bnm)studentB.loop(bnm)psw=input('Enterpassword:')num=input('Enternubmer:')studentB.seek(psw,bnm,num)目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.2.3关于类的属性1.给类的属性指定默认值借助上面的例6-4,在类Book的定义里,给出一个取默认值的属性“self.word='12345'”,并利用它作为口令的初始值。图6-4所示是两次运行的情形,一次是口令输入正确,将希望添加的书目(书名:xx,数量:3)添加到字典里;一次是口令输入错误,无法将书目添加到字典里去。这时的程序(缺少创建对象部分)如下:catalog={'aa':3,'bb':0,'cc':5,'dd':2,'ee':4,'ff':1,'gg':8,'hh':3}classBook(): globalcatalog def__init__(self,name): =name self.word='12345' #设置属性word默认的初始值,它不出现在参数表里6.2.3关于类的属性defloop(self,name): forkey,valueincatalog.items(): ifkey==name: ifvalue!=0: print('Thereisthebook') elifvalue==0: print('I\'msorrythatthebookhasbeenborrowed!') else: print('It\'apitythatthereisnobook!') breakdefseek(self,pword,name,value): ifpword!=self.word: #用到属性word默认的初始值 returnprint('Youdon\'thavetherighttoaddanewbook!') else: catalog[name]=value print(catalog)6.2.3关于类的属性bnm=input('Enterbookname:')studentB=Book(bnm)studentB.loop(bnm)psw=input('Enterpassword:')num=input('Enternubmer:')studentB.seek(psw,bnm,num)图6-42.对类的默认属性值的修改每种类都有自己的属性和方法,属性是从类的实例化中抽象出来的特征,由变量来描述;方法是从类的实例化中抽象出来的行为,由函数来描述。因此,修改类的属性值,就是修改描述它的变量的值,这是非常容易做到的事情。6.2.3关于类的属性修改默认属性值最直接的办法,是在创建的实例里进行。例如上面原先设置的默认属性值是:self.word='12345'只要在实例化studentB的程序里,增加一条语句:studentB.word='67890'于是,现在创建对象studentB时的程序就改写为:bnm=input('Enterbookname:')studentB=Book(bnm)studentB.loop(bnm)studentB.word='67890' #对默认属性值的修改psw=input('Enterpassword:')num=input('Enternubmer:')studentB.seek(psw,bnm,num)这时再运行程序,只有输入的口令是“67890”,才会允许将输入的书目和数量添加到字典catalog里;否则是通不过口令的检查的。6.2.3关于类的属性3.属性的增、删、改定义一个类并创建了它的对象后,可以对定义的类进行添加新属性、修改原有属性、删除已有属性的操作。下面的程序中,定义了一个名为Car的类,它有一个形参col,有两个默认参数:price=100000和name=“QQ”。#定义的类Car:classCar: def__init__(self,col): self.color=col self.price=100000 ='QQ'#程序主体:car1=Car('Red')print(,car1.color,car1.price)car1.price=110000car1.color='Yellow'car1.time='2016/08'print(,car1.color,car1.price,car1.time)delcar1.colorprint(,car1.color,car1.price,car1.time)print('End')6.2.3关于类的属性程序主体里,创建了一个名为car1的对象,随之通过语句:print(,car1.color,car1.price)输出信息“QQRed100000”。接着有语句:car1.price=110000car1.color='Yellow'car1.time='2016/08'前两条是修改已有属性price和color,后一条语句是增加新属性time(出厂时间)。这样的操作,使得程序主体中的第2条输出语句输出了这辆QQ的新信息。最后,通过语句:delcar1.color删除对象car1的属性color。第3条输出语句的执行,就会产生出错信息:AttributeError:'Car'objecthasnoattribute'color'6.2.3关于类的属性表示试图删除对象没有的属性,操作失败。图6-5记录了程序的整个执行过程。图6-54.属性的保护如上所述,在程序主体中可以对对象的属性进行增、删、改等操作。这种做法看似很方便,用起来也得心应手,但却违反了类的封装原则:数据使用的安全性。对象能够在类定义的外部随便访问其数据属性,就有可能修改它们,影响到类中提供的各种方法的正确运行,因为在类定义里给出的方法里面可能会用到属性变量。6.2.3关于类的属性类定义中的属性变量没有了私密性,就可能会带来各种想象不到的麻烦。考虑到这些问题,Python对类的属性提供了一种自我保护的简单办法。具体做法如下。classCar: def__init__(self,col): self.__color=col self.price=100000 ='QQ' defpri(self): print(self.__color)01OPTION02OPTION在需要具有私密性的属性变量名前,加上双下划线。在类定义里增加供程序设计人员访问属性变量的接口。仍以上面所定义的类Car来加以说明。car1=Car('Red')print(,car1.price)car1.pri()print('End')6.2.3关于类的属性假定要保护属性col的使用,不允许在程序主体内随意地访问它,那么可以在类定义中,在接收col的形参名前增加双下划线,即:self.__color另一方面,在类定义中写一个输出汽车颜色的方法:defpri(self): print(self.__color)以便在程序主体内能够输出汽车的颜色。classCar: def__init__(self,col): self.__color=col self.price=100000 ='QQ'先看一下在形参名前加上双下划线后的作用。这时整个程序是这样的:car1=Car('Red')print(,car1.price,car1.color)print('End')6.2.3关于类的属性运行该程序,结果如图6-6所示,给出出错信息:AttributeError:'Car'objecthasnoattribute'color'之所以会这样,是因为类定义里,在属性color变量名前加上了双下划线,Python对该变量进行了保护,不允许在类外通过“car1.color”直接访问该属性。为了能够在类定义外访问属性color,可以在类定义里给出访问该变量的函数,例如pri(),然后在程序主体里,把语句“print(,car1.price,car1.color)”改写成语句“print(,car1.price)”,增加语句“car1.pri()”。这样再运行,程序就正确了,如图6-7所示。图6-6图6-76.2.3关于类的属性classCar: def__init__(self,col): self.__color=col self.price=100000 ='QQ' defpri(self):
#增加的新函数
print(self.__color)car1=Car('Red')print(,car1.price) #去除直接访问属性color的内容car1.pri()
#增加调用类函数pri()的语句print('End')程序编写如下:目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.3.1Python里类的继承只要遵守Python里“继承”的规则,在程序设计中完成继承是不困难的。例如,简单定义了一个名为People的类,代码如下:“继承”,即承上启下。一个类,就是把它所要描述事物的特征(即属性)和行为(即方法)包装(也就是封装)在了一起。在继承关系中,已有的、设计好的类被称为“父类”或“基类”,新设计的类被称为“子类”或“派生类”。classPeople(): def__init__(self,name,nationality): =name self.nationality=nationality deftalk(self): print('Communicateinlanguage') defwalk(self): print('Walkuprightwithyourlegs!')6.3.1Python里类的继承由它派生出来的类,都应该有这样的属性和方法,也就是说都会继承这些属性和方法。下面定义一个名为Ch_people(中国人)的类:该类名为People,它有3个属性:self、name、nationality(国籍)。类中定义了两个方法,一是talk,调用它时输出信息“Communicateinlanguage”(用语言交流);二是walk,调用它时输出信息“Walkuprightwithyourlegs”(两腿直立行走)。classCh_people(People): #定义一个子类,名为Ch_people pass #表示该类不做什么事情在该类名字后面的括号里,填写了一个参数“People”,表明它是从类People派生出来的,要继承它的“衣钵”,也就是继承它的所有属性和方法。该类的类体里只有一条语句“pass”,表示虽然定义了这个新类,但它什么事情也不做,它没有自己新的属性,也没有自己新的方法,纯粹就是一个“空”的类。6.3.1Python里类的继承定义了类People和类Ch_people后,编写如下程序:#创建类People的一个对象peopApeopA=People('Zongdahua','china') print(peopA.name,peopA.nationality)peopA.talk()peopA.walk()#创建类Ch_people的另一个对象peopBpeopB=Ch_people('Zongdahua','china') print(peopB.name,peopB.nationality)peopB.talk()peopB.walk()执行整个程序,结果如图6-8所示。图6-86.3.1Python里类的继承上半部分是类People的对象peopA调用方法的结果;下半部分是类Ch_people的对象peopB调用方法的结果。由于类Ch_people是由类People派生出来的,它继承了类People的一切,自己又不多做任何事情,所以这两个对象的运行结果是完全一样的。为了便于描述,常会使用如下的名称。基类:也称“父类”,表示这种类是可以被别的类继承的。派生类:也称“子类”,表示这种类是一个继承别的类的类。例如,上面定义的类People,相对于类Ch_people来说,就是一个基类,即父类;而定义的类Ch_people,则是一个从类People派生出来的类,因此是一个子类。总结,在Python里,要从一个类里派生出另一个类,也就是一个类要继承另外一个类,只需将父类的名字放入该类定义的括号里即可。由于在子类的定义里只有一条pass语句,所以它将会从它的父类那里继承所有的特征和行为。目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.3.2在子类中改写父类的方法在继承机制下,允许子类改写父类中已经出现过的方法。通常,这种改写被称为“覆盖”。deftalk(self): print('Communicateinlanguage')加以改写(也就是“覆盖”),例如改写为:deftalk(self): print('ExchangeideasinChinese!')classCh_people(People): deftalk(self): print('ExchangeideasinChinese!')仍以类Ch_people这个由类People派生出来的类为例。类Ch_people当然是用语言来交流思想的,但类Ch_people是用特定的、自己民族的语言来交流思想的。因此,在定义类Ch_people时,可以将原先类People中的方法:这样,类Ch_people的定义就可以是:6.3.2在子类中改写父类的方法即先取消类Ch_people定义中的pass语句,然后再改写方法talk()。现在,整个程序变为:classPeople(): def__init__(self,name,nationality): =name self.nationality=nationality deftalk(self): print('Communicateinlanguage') defwalk(self): print('Walkontwolegs')classCh_people(People): deftalk(self): #改写父类中的同名方法talk() print('ExchangeideasinChinese!')peopA=People('Zongdahua','china')print(peopA.name,peopA.nationality)peopA.talk()peopA.walk()peopB=Ch_people('Zongdahua','china')print(peopB.name,peopB.nationality)peopB.talk()peopB.walk()6.3.2在子类中改写父类的方法运行该程序,结果就与图6-8有所不同了,如图6-9所示。图6-9这时在子类中,利用改写的初始化程序,就可以使子类增添自己特有的属性。例如,原先类People只有name和nationality两个属性,考虑到中国是一个多民族国家,希望在类Ch_people里,增加一个名为nation的属性。为此,可以在类Ch_people里,改写类People里的初始化程序,代码如下:classCh_people(People):def__init__(self,name,nationality,nation):=nameself.nationality=nationalityself.nation=nation6.3.2在子类中改写父类的方法当创建类Ch_people的对象时,Python就去执行该类如上的初始化程序,从创建处传递所需的实参给变量、self.nationality、self.nation,完成初始化工作,以保证整个程序正常运行。下面是整个程序的内容:classPeople(): def__init__(self,name,nationality): =name self.nationality=nationality deftalk(self): print('Communicateinlanguage') defwalk(self): print('Walkontwolegs')classCh_people(People):def__init__(self,name,nationality,nation):=nameself.nationality=nationalityself.nation=nationdeftalk(self):print('Exchangeideaswithnationallanguage!')peopA=People('Zongdahua','china')print(peopA.name,peopA.nationality)peopA.talk()peopA.walk()peopB=Ch_people('Zongdahua','china','Chinese')print(peopB.name,peopB.nationality,peopB.nation)peopB.talk()peopB.walk()6.3.2在子类中改写父类的方法运行该程序,结果就与图6-9又不一样了,如图6-10所示。图6-10目录导航6.1类和对象6.1.1类与对象的概念6.1.3对象:类的实例化6.2对类的进一步认识6.1.2Python中类的定义6.2.1关于初始化程序:__init__6.2.2关于参数:self6.2.3关于类的属性6.3类的继承6.4Python中类的导入6.3.1Python里类的继承6.3.2在子类中改写父类的方法6.4.1类的导入6.4.2导入多个类6.3.3内置函数super()6.3.4多重继承6.3.3内置函数super()实际应用中,肯定会有这样的情形发生:子类改写了父类的某个方法,后面却还要用到已被改写的原先父类的那个方法。Python的内置函数super()就可以解决这个问题,它建立起了子类与父类之间的联系,准确无误地在子类里找到父类,并实现对所需方法的调用。也就是说,如果需要在子类中调用父类的方法,那么可以使用内置函数super(),或者通过“父类名.方法名()”的方式,来达到这一目的。classParent(): defspeak(self): print('Thevoiceofthefather\'svoice!')classChild(Parent): passdad=Parent()son=Child()dad.speak()son.speak()例6-5一个全部继承的例子:6.3.3内置函数super()例中先定义了一个名为Parent的类,它有一个方法speak(),功能是输出信息“Thevoiceofthefather'svoice!”(父亲的说话声音)。接着定义一个名为Child的类,它无条件地继承了类Parent,因为它定义的括号里,有一个参数“Parent”,且类体里只有一条pass语句。程序里共有4条语句:第1条是dad=Parent(),它创建了一个名为dad的Parent对象;第2条语句是son=Child(),它创建了一个名为son的Child对象;第3条语句是对象dad调用方法speak();第4条语句是对象son调用方法speak()。由于子类Child全盘地继承了父类,所以这两个对象调用方法speak()的结果,都是输出信息“Thevoiceofthefather'svoice!”。6.3.3内置函数super()如图6-11最上面所示。图6-116.3.3内置函数super()classParent(): defspeak(self): print('Thevoiceofthefather\'svoice!')classChild(Parent): defspeak(self): print('Thevoiceoftheson\'svoice!')dad=Parent()son=Child()dad.speak()son.speak()把例6-5稍加修改,代码如下:例6-6一个覆盖父类方法的例子。6.3.3内置函数super()其他地方都没有动,只是子类虽然仍继承父类,但并没有全盘继承,而是修改了父类的方法speak(),即如果子类的对象再调用方法speak(),不是输出信息“Thevoiceofthefather'svoice!”,而是输出信息“Thevoiceoftheson'svoice!”。程序的运行结果如图6-11中间所示:父类的方法speak()被子类改写的方法speak()覆盖了。图6-116.3.3内置函数super()classParent(): defspeak(self): print('Thevoiceofthefather\'svoice!')classChild(Parent): defspeak(self): print('Thevoiceoftheson\'svoice!') super(Child,self).speak()dad=Parent()son=Child()dad.speak()son.speak()把例6-6稍加修改,代码如下:例6-7利用内置函数super()的例子6.3.3内置函数super()其他地方都没有动,只是子类虽然仍继承了父类,并改写了父类的方法speak(),但它的改写又与例6-6不同,即增加了一条内置函数super()调用方法speak()的语句“super(Child,self).speak()”。通常,内置函数super()有两个参数,第2个是self,表示自己;第1个则是一个类名,表示内置函数要调用的方法是这个类的父类的那个方法。例如在上述程序中,内置函数的第1个参数是Child,因此表明它要调用的那个方法speak(),是父类Child里的那个方法speak(),而不是自己覆盖的那个方法speak()。按照这样对内置函数super()的理解,我们来看程序中最后两条语句的执行结果。dad是类Parent的实例化,son是类Child的实例化。于是,执行语句dad.speak(),就是输出信息:Thevoiceofthefather'svoice!6.3.3内置函数super()执行语句son.speak(),就是要执行:print('Thevoiceoftheson\'svoice!')super(Child,self).speak()第1句好理解,即输出信息:Thevoiceoftheson'svoice!第2句的意思是要去调用类Child的父类的方法speak()。于是就去执行类Parent的方法speak(),即输出信息:Thevoiceofthefather'svoice!于是,该例的执行结果如图6-11最后面所示,即输出结果:Thevoiceofthefather'svoice!Thevoiceoftheson'svoice!Thevoiceofthefather'svoice!6.3.3内置函数super()在Python中继承有以下几个特点。01OPTION02OPTION03OPTION在继承中,父类的初始化程序__init__不会被自动调用,Python会先在其派生类的初始化程序中去寻找并调用,只有当子类中没有初始化程序时,才以父类的初始化程序作为自己的初始化程序。在子类对象里调用父类的方法时,需要以父类的类名作为前缀,并用“.”分隔,还需要带上self参数变量。但在一般的类中调用方法时,并不需要带上self参数。Python总是首先查找对应类的方法,如果它不能在派生类中找到对应的方法,它才开始到父类中去逐个查找。即先在子类中查找调用的方法,找不到才去父类中查找。6.3.3内置函数super()classParent(): def__init__(self): #父类的初始化程序
self.parent='I\'mtheparent.' print('Parent') deffun(self,message): #父类的方法fun() print('%sfromparent'%message)classChild(Parent): #子类的初始化程序
def__init__(self): super(Child,self).__init__() print('Child') deffun(self,message): #子类的方法fun() super(Child,self).fun(message) print('Childfunfuction') print(self.parent)#程序主体f_Child=Child()f_Child.fun('HelloPython!')下面是编写的整个程序,其运行结果如图6-12所示:例6-8阅读并对照运行结果,更进一步地理解内置函数super()。6.3.3内置函数super()整个程序分为3个部分:第1部分定义了名为Parent的类,它有一个初始化程序、一个名为fun的方法。第2部分定义了一个名为Child的类,类Parent是它的父类,它有自己的初始化程序,也有一个名为fun的方法,也就是说,这个方法覆盖了它父类中同名的方法。第3部分是程序主体,它仅由两条语句组成:第1条语句是对类Child的实例化,对象变量是f_Child;第2条语句是该对象以“HelloPython!”为参数,调用方法fun()。执行这样的两条简单语句,得到的输出信息居然会如图6-12所示。为什么会是这样呢?f_Child=Child()f_Child.fun('HelloPython!')6.3.3内置函数super()分析执行语句“f_Child=Child()”的情形。这是创建类Child的一个对象,对象的名字是f_Child。类Child继承了类Parent,且有自己的初始化程序(也就是它改写了父类的初始化程序),因此创建类Child的对象时,不会去执行父类的初始化程序,而是去执行自己的初始化程序:def__init__(self): super(Child,self).__init__() print('Child')(1)子类初始化程序的第1条语句:super(Child,self).__init__()。其含义是通过内置函数super()调用名为__init__()的初始化程序。父类有初始化程序,子类也有初始化程序,这里是调用哪一个初始化程序呢?这将由内置函数super()里给出的第1个参数“Child”来指明,即调用类Child的父类的初始化程序。于是,Python转而去执行父类Parent的初始化程序:6.3.3内置函数super()def__init__(self): self.parent='I\'mtheparent.' print('Parent')(2)子类初始化程序的第2条语句:print('Child')。父类的初始化程序的第1条语句是为变量self.parent赋初值“I’mtheparent”。第2条语句输出信息“Parent”。于是,在图6-12中输出的第1条信息是“Parent”。含义是输出信息“Child”。于是,在图6-12里接着输出了“Child”。至此,语句f_Child=Child()就分析并执行完了。分析执行语句“f_Child.fun('HelloPython!')”的情形。该语句的意思是由对象f_Child调用(自己的)方法fun(),传递的参数是“HelloPython!”。对象f_Child的方法fun()是:6.3.3内置函数super()deffun(self,message): super(Child,self).fun(message) print('Childfunfuction') print(self.parent)共有3条语句,现在分析如下。01OPTION子类fun()方法的第1条语句:super(Child,self).fun(message)。该语句是一条内置函数super(),它要通过传递参数message去调用名为fun的方法。同样地,父类有名为fun的方法,子类也有名为fun的方法。到底应该调用哪一个将由内置函数super()括号内的第1个参数来定。很清楚,是要调用父类Parent的方法fun()。于是,转而去看父类Parent的方法fun():deffun(self,message): print('%sfromparent'%message)6.3.3内置函数super()02OPTION子类方法fun()的第2条语句:print('Childfunfuction')。这条语句功能简单,就是输出信息:Childfunfuction它的功能就是把message传递的信息输出来。现在message携带的信息是“HelloPython!”,因此在窗口里紧接着会输出信息:HelloPython!Fromparent(见图6-12输出的第
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版数据中心机房安全防护系统建设合同
- 2025版集装箱租赁与物流信息化服务合同范本3篇
- 包头钢铁职业技术学院《无线通信》2023-2024学年第一学期期末试卷
- 2024年酒吧厨师与餐饮服务合同
- 蚌埠经济技术职业学院《分子生物学(双语)》2023-2024学年第一学期期末试卷
- 2025版水路货物运输合同绿色航运服务协议3篇
- 2024年高端人才聘请与保密协议
- 2025年度房地产项目消防验收及报建代理服务协议2篇
- 2024年监控设备销售合同样本版B版
- 2024年猫咪宠物食品买卖合同
- 2019电子保单业务规范
- 学堂乐歌 说课课件-2023-2024学年高中音乐人音版(2019) 必修 音乐鉴赏
- 幕墙工程材料组织、运输装卸和垂直运输方案
- 灌溉用水循环利用技术
- 急停急起运球教学设计
- 2024年江西省三校生高职英语高考试卷
- 中国古代文学智慧树知到期末考试答案章节答案2024年广州大学
- 重庆市南岸区2022-2023学年五年级上学期期末语文试卷
- 现浇钢筋混凝土整体式肋梁楼盖结构-课程设计
- 挂篮施工及安全控制连续梁施工安全培训课件
- 学生学习概览StudentLearningProfile
评论
0/150
提交评论