当前位置: 首页 > 后端技术 > Python

[Python]-8-对象和类(下)

时间:2023-03-25 22:37:24 Python

简介本文介绍了Python如何将函数和数据集成到类中,并通过对象名来访问它们。文章目录0×9。类0×10的多重继承。自定义类0×11。枚举类0×12。动态类0×9。类的多重继承python中的类提供了多重继承的功能,比如C类可以同时继承B类和A类,C类可以使用B类和A类的所有属性和方法,请看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA(object):def_Print_website(self):print("www.qingsword.com")########classB(object):def_Print_HelloWorld(self):print("HelloWorld")#########多重继承只需要在子类括号中用逗号分隔要继承的类classC(A,B):def__init__(self):self.name="qingsword"#因为C类同时继承了A类和B类,它的实例可以使用A类和B类的所有方法和属性x=C()x._Print_website()x._Print_HelloWorld()print(x.name)#程序输出www.qingsword.comHelloWorldqingsword0×10。自定义类当我们实例化一个类对象并使用print打印类对象时,往往会打印出一个类名和内存地址。请看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA:#------def__init__(self,name="qingsword"):self.name=namex=A("www.qingsword.com")print(x)#程序输出<__main__.Aobjectat0x7f41d6039f28>为了让打印输出更友好,python提供了两个函数__str__和__repr__,前者可以让print打印一个对象,输出这个函数下自定义的返回信息;后者是为了让调试器(IDLE提示环境等)在调试过程中直接输入对象时打印出我们自定义的信息,而两者通常写在同一个返回信息中,比如,把上面的例子改成如下:#!/usr/bin/envpython#coding=utf-8########classA:#--------def__init__(self,name="qingsword"):self.name=name#--------def__str__(self):"""自定义对象返回信息"""return"ClassAObjectself.name=%s"%自己。name#直接让repr函数等于str__repr__=__str__x=A("www.qingsword.com")print(x)#程序输出ClassAObjectself.name=www.qingsword.com除了输出信息自定义对象,如果用A类作为for循环,Python提供了__iter__方法,返回一个可迭代对象,可以用for循环迭代这个对象,for会不断调用类的__next__()方法object获取下一个循环的值,直到遇到StopIteration错误(或break)退出循环,一个类可以自定义为可迭代类,看下面的例子:#!/usr/bin/envpython#coding=utf-8########classFib(object):#--------def__init__(self):self.a,self.b=0,1#实例本身是迭代对象,所以iter函数返回自己def__iter__(self):returnself#如果对象中的a属性值大于20,则停止循环,否则返回每次循环得到的a值def__next__(选择f):self.a,self.b=self.b,self.a+self.bifself.a>20:raiseStopIteration()returnreturnself.ax=Fib()foryinx:print(y)#这是一个Fibonacci迭代类,程序输出如下11235813#如果去掉类中的self.a>20判断,可以将for循环改为foryinx:#可以手动指定一个迭代范围ify<20:print(y)else:breaktheiterable上面的类看起来更像是一个生成器,但是现在我们不能像列表一样使用索引来获取单个值。为此,python提供了__getitem__方法来实现获取单个值的类。修改以上程序如下:#!/usr/bin/envpython#coding=utf-8########classFib(object):#根据输入的n,返回x的值def__getitem__(self,n):#x和y只是函数中的一个局部变量。每次调用该函数,都会重置为1x,y=1,1whilen>0:x,y=y,x+yn-=1returnxx=Fib()print(x[2])print(x[12])#Programoutput2233如果想给这个类添加slice函数,可以修改上面的代码如下:#!/usr/bin/envpython#coding=utf-8########classFib(object):def__getitem__(self,n):#判断输入是片段类型还是索引值ifisinstance(n,int):x,y=1,1whilen>0:x,y=y,x+yn-=1returnxelfisinstance(n,slice):#获取分片的起始值=n.startstop=n.如果开始为无则停止:start=0x,y=1,1L=[]forainrange(stop):ifa>=start:L.append(x)x,y=y,x+y(xreturn[L2)(x)([:12])print(x[6:12])#程序输出233[1,1,2,3,5,8,13,21,34,55,89,144][13,21,34,55,89,144]上面这个简单的切片器并没有处理负数和切片步长,只是提供一个思路告诉大家,通过这个自定义函数,一个类可以自定义成任何数据类型,可以是列表字典元组等。通常我们在调用对象的某个属性和方法时,如果该属性和方法不存在,就会报错,如下图:#!/usr/bin/envpython#coding=utf-8########classA(object):def__init__(self):self._path=""x=A()#调用_path没有问题print(x._path)#但是调用一个子属性就不行exist会报错,提示子属性不存在。print(x.sub)python提供了一个__getattr__函数,它接收一个不存在的属性或方法名,然后返回一个我们设置的值。如果我们不定义返回值,它将返回None,这比直接抛出错误要友好得多,看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA(object):def__init__(self):self._path=""def__getattr__(self,attr):ifattr=="blog":return"www.qingsword.com"ur"elifokret="doit":"#A函数返回值elifattr=="age":returnlambda:23x=A()print(x.blog)print(x.doit)print(x.age())print(x.nothing)#programOutputwww.qingsword.comok23None请注意,__getattr__函数只有在类中没有定义这些属性值时才接收这些属性值,并根据我们自己的判断返回值。如果类中有这些属性或方法,它们将被优先使用。另外,如果这个函数接收到一个没有定义返回值的名称,它将默认返回None,除非手动抛出一个AttributeError。比如在if的末尾加入如下代码,再次运行时会抛出异常,提示什么都不存在:else:raiseAttributeError("Wrongattributeormethodname:%s"%attr)__getattr__最常见的应用是链式调用,请看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA(object):def__init__(self,path=""):self._path=pathprint('self._pathis%s'%self._path)def__getattr__(self,attr):returnA("%s/%s"%(self._path,attr))def__str__(self):returnself._path__repr__=__str__x=A()print(x.www.qingsword.com)#programoutput#x=A()outputself._pathis#x因为有.www中没有www属性,调用__getattr__函数,传入一个www,然后递归调用A。这一步相当于A("/www")self._path是/www#x.www.qingsword会再次激活__getattr__函数,因为www没有qingsword属性,所以self._path是/www/qingswordself._path是/www/qingsword/com/www/qingsword/com当我们实例化一个对象时,我们可以通过“对象名.属性名”来使用属性,或者通过“对象名.方法名()”来调用方法.另外,python提供了一个__call__函数,可以让对象调用For自己,请看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA(object):def__init__(self,name="qingsword"):self.name=namedef__call__(self):print("Mynameis%s"%self.name)x=A()#直接调用对象本身相当于在对象中执行__call__函数,就像x本身是一个函数而不是像x()这样的类对象#Output"Mynameisqingsword"#我们可以使用callable()函数来判断一个对象是否可调用。该函数返回一个布尔值。如果我们把A类中的call函数注释掉,那么下面第一句就是ReturnFalse,可以试试print(callable(x))#Trueprint(callable(str))#Trueprint(callable([1,2,3]))#False0×11。枚举类Python3.0之后,提供了一个枚举类型,弥补了Python2中枚举的不足。首先来看一个简单的枚举例子:#!/usr/bin/envpython#coding=utf-8#ImportenumerationmodulefromenumimportEnum#定义一个枚举变量mm=Enum('Month',('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'))#使用不同的方法获取枚举中的名称和值,与列表不同的是,枚举的索引从1开始print(m(1))#Month。Janprint(m.Mar)#Month.Marprint(m["Oct"])#Month.Octprint(m.Mar.value)#3#打印出枚举中的所有名字x=len(m.__members__)#Get枚举数foriinrange(1,x+1):print(m(i))#ProgramoutputMonth.JanMonth.FebMonth.MarMonth.AprMonth.MayMonth.JunMonth.JulMonth.AugMonth.SepMonth.OctMonth.NovMonth.dec#获取枚举成员的名字和全名,m.__members__.items()会将枚举拆分成一个列表,每个元素是一个元组,每个元组包含一个字符串和一个字典类型,拆分后的列表长这样this[("Jan",),("Feb",)....],然后使用名称接收第一个参数(Jan),成员接收第二个参数(',member,',',member.value)#programOutputJan=>Month.Jan,1Feb=>Month.Feb,2Mar=>Month.Mar,3Apr=>Month.Apr,4May=>Month.May,5Jun=>Month.Jun,6Jul=>Month.Jul,7Aug=>Month.Aug,8Sep=>Month.Sep,9Oct=>Month.Oct,10Nov=>Month.Nov,11Dec=>Month.Dec,12了解了枚举的创建和访问方法之后,让我们看看枚举类的创建方法:#!/usr/bin/envpython#coding=utf-8fromenumimportEnum,unique@unique#unique装饰器可以保证枚举类Week(Enum):"""继承枚举类,可以指定每个名字的值"""Sun=0#设置起始索引为0Mon=1Tue=2Wed=3Thu=4Fri=5Sat=6#获取方式相同例如print(Week(0))print(Week.Sat.value)print(Week["Thu"])#程序输出Week.Sun6Week.Thu在Python3中,枚举类常用于定义常量,因为枚举中每个名字对应的值是不可变的,例如:#!/usr/bin/envpython#coding=utf-8fromenumimportEnum,unique@uniqueclassconst(Enum):"""常量枚举类"""website="www.qingsword.com"author="qingsword"pi=3.1415print(const.website.value)print(const("qingsword"))print(const["pi"].value)#程序输出www.qingsword.comconst.author3.14150×12.动态类动态语言和静态语言在创建类时也是有区别的。动态语言类的创建是在程序执行后依次执行类中的语句,动态创建一个类,而静态语言在编译时就已经创建了这些类;我们用类型来判断一个类。类型,经常看到类的类型是type,这跟动态类的创建有什么关系呢?请看下面的例子:#!/usr/bin/envpython#coding=utf-8########classA(object):defWebsite(self):print("www.qingsword.com")x=A()print(type(A))print(type(x))#程序输出,从输出可以看出,x是类A的一个实例,但是类A本身就是一个类type其实type方法不仅可以判断对象的类型,python也可以用它来动态创建类,使用type动态创建的类和直接使用class创建的class是的,这就是为什么上面的type判断classA的类型是class类型的原因。其实两者可以算同一种类型。下面的例子演示了如何通过类型动态创建类:#!/usr/bin/envpython#coding=utf-8defWebsite(self):print("www.qingsword.com")defSayHello(self,name="qingsword"):print("Hello%s"%name)#B指向一个Type动态创建的类,所以B代表类(A)本身#type语法:#type("类名",(继承类list,...),dict(classmethodname=boundmethodname,...))#利用这个方法,我们可以动态地将外部方法绑定到一个类上,从而在运行时创建一个类B=type("A",(object,),dict(web=网站,hello=SayHello))x=B()print(x)print(type(B))x.web()x.hello("xxx")#程序输出<__main__.Aobjectat0x7f7d70821048>www.qingsword.comHelloxxx