版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章类面向过程编程思想结构化编程,是自顶向下逐步分解的设计过程程序=数据结构+算法,更关注算法效率最小单元是函数不足之处:设计不够直观:设计过程需按功能划分,且各个功能没有主体概念,不利于大型软件的设计适应性较差:数据与数据操作逻辑相对分散,容易导致代码间耦合性较高,从而导致代码修改的级联影响范围较大,程序的健壮性和维护性不太友好,不太适合大型复杂系统设计面向对象编程思想对现实世界中的事物对象进行抽象描述,设计过程更关注各个对象的状态属性和功能行为,通过对象间协作完成功能最小单元是类类=成员变量(属性数据)+方法(行为)特点:通过封装、继承和多态机制构建面向对象的软件系统火车站购票案例火车站购票案例面向过程和面向对象面向对象与面向过程:二者相辅相成,并不是对立的。解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系、方便我们分析整个系统;具体到微观操作,仍然使用面向过程方式来处理。类的定义在Python中一切皆对象,面向对象通过封装、继承、多态特性构建一个面向对象的设计体系,通过对象之间的协作完成具体的业务逻辑和功能对象之间的协作存在两个概念:服务提供者和服务使用者类的定义本质上是一种封装,是对某种事物的抽象,包括数据抽象和行为抽象,数据抽象是通过封装抽象事物的状态、数据,行为抽象是指封装操作数据的行为,从而隐藏对象内部的复杂性,只对外公开简单的服务访问接口类定义了抽象事物的状态(属性)和行为(函数),对象则是类的具体实体,也可以称为类的实例类的定义类定义与函数定义一样,必须被执行才会起作用,当进入类定义时,将创建一个命名空间,并且该命名空间从属于局部作用域,当从结尾处正常离开类定义时,将创建一个类对象。类的声明采用class保留字,语法形式如下:class<类名>: <类体>类的定义类对象支持两种操作:属性引用和实例化。属性引用采用“.”运算符,即形如:。属性引用中有效的属性名称是类对象被创建时存在于类命名空间中的所有名称,包括类属性和方法类的实例化使用函数表示法,可以把类对象当作一个函数,该函数无参数,返回类型是一个该类的新实例。如实例化一个空的Goods对象形如:goods=Goods()类的定义实例化操作会创建一个空对象,如果需要创建带有特定初始状态的自定义实例,则通过定义一个名为__init__的特殊方法。当一个类定义了__init__方法时,类的实例化操作会自动为新创建的类实例调用__init__方法,其中__init__可接受参数用于实例的初始化。类的实例化得到的实例对象支持属性引用操作,有效的属性名称包括两种:数据属性和方法。数据属性即为实例属性,数据属性不需要声明,在第一次被赋值时产生。实例对象的有效方法名称依赖于其所属的类,一个类中,所有是函数对象的属性都定义了其实例的对应方法。实例属性类定义中的属性用于描述抽象事物的特征或者状态,分为实例属性和类属性实例属性是指实例对象的属性,同一个类的不同对象,其实例属性是相互隔离的,独属于该实例对象实例属性无需声明,在赋值时创建,主要是在构造方法__init__中定义,同时在定义和在实例方法中访问该属性时往往以self为前缀实例属性也可以在非构造方法中以self前缀进行定义实例属性,即实例属性的定义是在实例方法中在类的外部访问这些实例属性则需要首先创建类的实例对象,然后通过形如的形式进行访问或者更新实例属性示例定义一个超市中的普通商品类,该商品类具有商品名称、商品价格、商品库存和商品简介:
classGoods:def__init__(self,name,price,count,desc):=nameself.price=priceself.count=countself.desc=descnotebook=Goods("笔记本",3.5,10,"100页的晨光笔记本")print()="晨光笔记本"print()
类属性类属性是从属于类的,为该类的所有对象进行共享该变量,其生命周期是在执行了类的定义开始有效类属性的定义不会在任何成员方法之内,一般定义在类的开头,也可定义在类的中间,只要是在类的命名空间可访问的地方即可在类的外部,类属性可以通过类名或者类的实例对象引用访问。类属性示例定义个面包类,面包类一般有有效期属性,该属性是属于一个实例对象的。而有效期属性一般是datetime类型,且在构造时一般是输入的日期字符串,将日期字符串格式化为datetime对象的日期格式这一属性应为面包类的所有对象共享,所有的面包类统一采用一个格式化字符串,该属性则应定义为类属性
classBread:format_str="%Y/%m/%d%H:%M:%S"def__init__(self,validDate):print(self.format_str)self.validDate=datetime.strptime(validDate,self.format_str)print(Bread.format_str)bread=Bread("2021/05/0417:57:0")print(bread.validDate)
属性访问约定Python不同于如Java等面向对象语言,其没有提供访问权限控制符,即仅对象内部能访问的“私有”实例变量在Python中并不存在Python中的访问权限控制采用的是通用约定形式,实际访问控制取决于类的使用者,但是建议大家遵守该约定,便于代码的维护。属性访问约定该约定根据名称中下划线前缀可分为如下四种情况,其中名称包括变量和方法:如果名称无下划线前缀,则约定该变量或方法是公开访问的,类的实现者即服务提供者会尽可能保证该部分稳定不变;如果名称前有一个下划线,则约定该变量或方法应视为非公有部分,属于实现细节,后续重构优化迭代可能不经通知而发生改变;属性访问约定如果名称前面有两个下划线,则约定该变量或方法应视为私有成员,在类的外部不可直接访问,该类型的成员在类定义的内部将通过重命名形式在语法上提供支持,将__var成员按_className_var形式重命名该变量,故如果在类的外部直接访问__var则会报错示例:执行print语句会报错如下:AttributeError:typeobject'TestPrivate'hasnoattribute'__name'
classTestPrivate:__name="testprivate"print(TestPrivate.__name)
属性访问约定如果名称前后均有两个下划线,则约定该部分为类的内置属性。如__name__查看类名等,部分内置属性如下:特殊属性含义object.__dict__对象的属性字典instance.__class__对象所属的类class.__bases__类的基类元组class.__base__类的基类class.__name__类的名称class.__qualname__类的限定名称class.__mro__方法查找顺序,基类元组class.mro()同上,可被子类重写class.__subclasses__()子类列表property在一些业务场景中,我们希望在对实例属性的赋值、更新或访问进行控制,如执行类型检查、权限控制、值合法性验证等,此时可以采用property装饰器通过property装饰器修饰的方法称为property属性,该属性可以有三个互相关联的方法,这三个关联方法必须具有相同的名称,分别通过采用@property、@func.setter、@func.deleter修饰,分别对应属性的获取、设置、删除,其中func值为@property修饰的方法名采用property修饰的方法可以像普通属性的形式进行访问,同时会根据访问方式的不同自动触发getter、setter和deleter方法。property示例商品的价格属性为例,通过property进行类型检查和值合法性验证:
classGoods:def__init__(self,price):self.price=price@propertydefprice(self):print("executegetter")returnself.__price@price.setterdefprice(self,rawPrice):print("executesetter")ifnotisinstance(rawPrice,float)andnotisinstance(rawPrice,int):raiseTypeError("价格类型不合法")ifrawPrice<0:raiseValueError("价格不能小于0")self.__price=rawPrice@price.deleterdefprice(self):print("executedeleter")raiseAttributeError("商品对象不能删除价格属性")
notebook=Goods(3.5)print(notebook.price)notebook.price=4print(notebook.price)delnotebook.price执行结果为:executesetterexecutegetter3.5executesetterexecutegetter4executedeleterTraceback(mostrecentcalllast):File"TestProperty.py",line28,in<module>delnotebook.priceFile"TestProperty.py",line22,inpriceraiseAttributeError("商品对象不能删除价格属性")AttributeError:商品对象不能删除价格属性
property示例对于已经存在的getter和setter方法,同样也可以将它们定义为property
classGoods:def__init__(self,price):self.set_price(price)defget_price(self):print("executegetter")returnself.__pricedefset_price(self,rawPrice):print("executesetter")ifnotisinstance(rawPrice,float)andnotisinstance(rawPrice,int):raiseTypeError("价格类型不合法")ifrawPrice<0:raiseValueError("价格不能小于0")self.__price=rawPricedefdel_price(self):print("executedeleter")raiseAttributeError("商品对象不能删除价格属性")
price=property(get_price,set_price,del_price)
property查看property的构造方法可知,property属性本质上就是把getter、setter、deleter方法绑定到property自身所持有的fget、fset和fdel三个属性中。property的构造方法签名如下所示:def__init__(self,fget=None,fset=None,fdel=None,doc=None)property也可以用于定义一些需要计算的属性,该类属性并不会实际保存起来,而是根据需要完成计算,从而使得可统一采用属性形式访问,而无需将属性和方法调用混合使用property示例长方形计算面积和周长为例
classRectangle:def__init__(self,width,height):self.__width=widthself.__height=height@propertydefarea(self):returnself.__width*self.__height@propertydefperimeter(self):return2*(self.__width+self.__height)rect=Rectangle(2,3)print(rect.area)print(rect.perimeter)
方法类的定义封装了对数据的操作逻辑或者是抽象事物的功能行为,这些在类定义中采用方法形式存在。类的方法分为:实例方法、静态方法和类方法三种实例方法是指从属于实例对象的方法,调用该方法需要实例化对象,该方法可以访问实例属性和类属性一般实例方法声明语法格式如下:def方法名(self,[形参列表]):
函数体实例方法调用格式如下:
对象.方法名([实参列表])实例方法示例商品类为例,商品类就有查看商品详情和计算支付价格行为,而商品详情设计的属性和价格为实例属性,即方法操作的是具体的实例对象,故商品类应该具有查看详情和支付价格计算两个实例方法:
classGoods:def__init__(self,name,price,count,desc):self._name=nameself._price=priceself._count=countself._desc=descdefdetail(self):print("商品名称:{}\n单价:{}元/件\n库存:{}件\n商品描述:{}".format(self._name,self._price,self._count,self._desc))defgetPayPrice(self,amount):returnself._price*amountnotebook=Goods("笔记本",3.5,10,"100页的笔记本")notebook.detail()print(notebook.getPayPrice(3))
静态方法静态方法是不依赖于类实例对象和类对象的方法,一般是跟类有一定关系的功能,但是在运行时又不需要实例对象和类对象参与,如一些与类相关的工具方法,虽然可以定义到类的外面,但是可能该工具方法只在该类中进行调用,即仅服务于该类,此时采用定义静态方法形式将该方法包含在类的命名空间中,提高代码内聚。静态方法采用@staticmethod装饰,另因为静态方法没有接收实例对象和类对象的引用参数,故无法访问类的实例属性和类属性,类的实例和类对象均可调用静态方法。类方法类方法是采用@classmethod装饰的方法,其中的第一个参数为当前调用类对象的引用,一般命名为cls,类方法操作的是类对象而非实例对象,故可操作类属性,不可访问实例属性。如果需要在实例化类对象前提供某些功能,则需要采用类方法或者静态方法类方法对于实现多种构造方法提供了很好的帮助。类方法示例
classDate:def__init__(self,day=0,month=0,year=0):self.day=dayself.month=monthself.year=yeardefdisplay(self):return"{0}-{1}-{2}".format(self.year,self.month,self.day)@classmethoddeffrom_string(cls,date_as_string):year,month,day=map(int,date_as_string.split('-'))dateObj=cls(day,month,year)returndateObj@staticmethoddefis_date_valid(date_as_string):year,month,day=map(int,date_as_string.split('-'))returnday<=31andmonth<=12andyear<=3999
date=Date(2021,5,31)print(date)#1print(date.display)#2print(date.from_string)#3print(date.is_date_valid)#4dateObj=Date.from_string('2021-05-31’)#5print(dateObj)#6is_date=Date.is_date_valid('2021-05-31')print(is_date)执行结果为:<__main__.Dateobjectat0x000002038392F438><boundmethodDate.displayof<__main__.Dateobjectat0x000002038392F438>><boundmethodDate.from_stringof<class'__main__.Date’>><functionDate.is_date_validat0x0000020383923D90><__main__.Dateobjectat0x000002038392F550>True
构造方法示例类通过__init__方法实例化对象,__init__方法的第一个参数是类的实例对象,那么传递给__init__的实例对象是如何得到的呢?在Python中实例对象的创建分为两步:首先是创建对象,由__new__()负责对象的创建然后是初始化对象,由
__init__方法负责对象的初始化
classTest:def__init__(self):print("__init__")print(self)def__new__(cls):print("__new__")self=super(Test,cls).__new__(cls)print(self)returnselftest=Test()
__new__<__main__.Testobjectat0x0000021022395D30>__init__<__main__.Testobjectat0x0000021022395D30>
构造方法__new__方法先于__init__()方法执行,且__init__中的self就是__new__方法返回的,即__new__方法的返回值就是类的实例对象,这个实例对象会传递给__init__方法中定义的self参数,用于后续对该实例对象进行正确初始化。如果__new__方法不返回值或者返回None,那么__init__将不会得到调用,因为实例对象没有创建出来,自然也无需初始化。
__new__方法是用来控制对象的创建,如对象创建前后需要做的一些行为,也可以用于单例模式这种控制对象创建的过程__new__实现单例模式示例
classSingletonTest:_singleton=Nonedef__new__(cls):ifnotcls._singleton:cls._singleton=object.__new__(cls)returncls._singletonobj1=SingletonTest()obj2=SingletonTest()print(obj1)print(obj2)
<__main__.SingletonTestobjectat0x000001D8B4255C88><__main__.SingletonTestobjectat0x000001D8B4255C88>
析构方法对象有创建,自然也有删除回收资源,在python中执行对象删除的方法是__del__()方法,称为析构方法,与__init__()方法类似,原型为:def__del__(self):析构方法一般有两种调用时机:当对象在某个作用域中调用完成,没有其他地方引用时会调用析构方法回收资源释放内存空间采用del指令删除特定对象,此时如果该对象的引用次数为0,则会调用析构方法回收资源析构方法示例
classDestructorTest:def__init__(self):print("初始化对象")def__del__(self):print("删除对象")obj=DestructorTest()delobjprint("---end---")
初始化对象删除对象---end---
继承继承是面向对象三大特性之一,继承机制源于现实世界中许多对象之间具有共性和派生关系,通过继承机制可解决代码复用和维护代码的一致性。继承是子类继承父类共性的东西,包括特征和行为,使得子类对象具有父类的属性和方法,即在子类中可访问父类的属性和调用父类的方法。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。python支持多重继承,即一个子类可以继承多个父类。继承子类的定义格式如下:classDerive(基类1,基类2….):
类体示例:面包商品继承普通商品,见Pycharm,继承Python支持继承且支持多继承,故子类访问的方法可能定义在当前类,也可能来自于不同的基类,故在调用方法时需要对当前类和基类进行搜索以确定方法所在的位置。搜索查找方法的顺序称为方法解析顺序(methodresolutionorder,MRO)在Python3中,如类A可通过A.mro()或A.__mro__查看类A的MRO信息。Python提供了多种MRO,其中Python3采用的C3算法得到的MRO,用于保证继承关系中线性化的单调性原则和局部优先级。C3算法首先把类C的MRO记为L[C]=[C1,C2,…,CN],其中C1称为L[C]的头,其余元素[C2,…,CN]称为尾如果一个类C继承自基类B1,B2,…,BN,即classC(B1,B2,…BN),则L[C]通过以下两步计算生成:L[object]=[object]L[C(B1…BN)]=C+merge(L[B1],L[B2],…L[BN],B1,B2,…BN)说明:B1B2…BN表示[B1,B2,…BN];[C]+[B1,B2,...,BN]=[C,B1,B2,…BN];C3算法merge操作为C3算法核心,具体流程为:检查第一个列表的头元素(如L[B1]的头),记为H;若H未出现在其他列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤a,否则取出下一个列表的头部记为H,继续该步骤;重复上述步骤,直到列表为空或者不能再找出可输出的元素,如果是前一种情况,则算法结束,如果是后一种情况,则说明无法构建继承关系,Python将拒绝创建C类并抛出异常。C3算法示例
classX:passclassY:passclassA(X,Y):passclassB(Y,X):passclassC(A,B):pass
C3算法示例其中类C的MRO计算过程如下:1.L[object]=[object];L[X]=[X,object];L[Y]=[Y,object]2.L[A]=[A]+merge(L[X],L[Y],[X],[Y])
=[A]+merge([X,object],[Y,object],[X],[Y])=[A,X]+merge([object],[Y,object],[Y])=[A,X,Y]+merge([object],[object])=[A,X,Y,object]C3算法示例同理得到L[B]=[B,Y,X,object];最后计算类C的MRO:L[C]=[C]+merge(L[A],L[B],[A],[B])=[C]+merge([A,X,Y,object],[B,Y,X,object],[A],[B])=[C,A]+merge([X,Y,object],[B,Y,X,object],[B])=[C,A,B]+merge([X,Y,object],[Y,X,object])此时merge操作中无法找到新的输出元素,因为第一个列表[X,Y,object]的头元素X出现在第二个列表[Y,X,object]的尾部,而第二个列表的头元素Y出现在第一个类别的尾部,故没有符合条件的元素可输出继续迭代,此时Python会拒绝创建类C并抛出异常方法重写子类继承了父类的方法,但是子类有其自己的差异性,有些行为不同于父类,此时需要重写父类的方法,如前面Bread类detail方法,因为Bread类多了有效期属性validDate,故查看商品详情的时候需要对detail方法重写。因为Python中不存在方法重载,多个同名的方法,则以最后一个为准,故子类如果有与父类同名的方法,根据MRO可知,首先查找子类,在子类中找到了同名方法名,则搜索停止不再到父类搜索,调用时如果参数列表不符合则会报错。方法重写在子类想要调用父类方法时,可直接采用BaseClassName.method()的方式进行调用,也可以采用super().method()的方法调用这两种方式在Python单继承时效果一致,多继承时super与父类没有直接关联,而是获取继承顺序中的下一个类,在多继承时,方法的查找顺序参考MRO顺序,可通过ClassName.__mro__查看该顺序示例:见Pycharmsupersuper是一个类,而非关键字和函数,当我们调用super()时,会实例化一个super类得到一个super实例,该
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 郑州2025年河南新密市招聘教师100人笔试历年参考题库附带答案详解
- 衡水2025年河北衡水学院选聘工作人员21人笔试历年参考题库附带答案详解
- 绍兴浙江绍兴博物馆编外人员招聘笔试历年参考题库附带答案详解
- 湘西2025年湖南湘西州泸溪县招聘劳务派遣制教师72人笔试历年参考题库附带答案详解
- 海南2025年海南琼台师范学院附属桂林洋幼儿园招聘员额制工作人员笔试历年参考题库附带答案详解
- 河南2025年河南省直第三人民医院招聘30人笔试历年参考题库附带答案详解
- 杭州2025年浙江杭州市西湖区人民检察院编外人员招聘笔试历年参考题库附带答案详解
- 抚州2025年江西抚州市东乡区城区中学临聘教师招聘100人笔试历年参考题库附带答案详解
- 广西2025年广西职业技术学院高层次人才招聘21人笔试历年参考题库附带答案详解
- 山东2025年山东体育学院招聘博士工作人员(第三批)笔试历年参考题库附带答案详解
- 外科院感课件
- 2025国家核安保技术中心招聘笔试历年常考点试题专练附带答案详解试卷3套
- 12158-2024防止静电事故要求
- 酒吧内保年终总结
- 儿童讲解员礼仪
- 文物建筑勘查设计取费标准(2020年版)
- DB14∕T2248-2020 《煤矿安全风险分级管控和隐患排查治理双重预防机制实施规范》
- 千古奇文《初心》原文
- 失禁相关性皮炎与压力性损伤的区分鉴别
- 铝合金门窗设计说明
- 食品行业仓库盘点制度及流程
评论
0/150
提交评论