引言

这篇文章介绍Python如何将函数与数据整合成类,并通过一个对象名称来拜访它们。

文章目录

0×9.类的多重继承
0×10.定制类
0×11.枚举类
0×12.动静类

0×9.类的多重继承

python中的类提供了多重继承的性能,比方C类能够继承B类同时又能继承A类,C类可能应用B和A类所有属性和办法,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A(object):    def _Print_website(self):        print("www.qingsword.com")########class B(object):    def _Print_HelloWorld(self):        print("Hello World")#########多重继承只须要在子类括号中用逗号分隔须要继承的类即可class C(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.comHello Worldqingsword

0×10.定制类

当咱们实例化一个类对象后,应用print打印这个类对象,往往会打印出一个类名称和内存地址,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A:    #--------    def __init__(self,name="qingsword"):        self.name=namex=A("www.qingsword.com")print(x)#程序输入<__main__.A object at 0x7f41d6039f28>

为了让打印输出更加的敌对,python提供了两个函数__str__和__repr__,前者能够让print打印一个对象时,输入这个函数下定制的返回信息;后者是在调试过程中让调试程序(IDLE提示符环境等)在间接输出对象时,可能打印出咱们定制的信息,这两者通常写入雷同的返回信息,例如,将下面的实例更改如下:

#!/usr/bin/env python#coding=utf-8########class A:    #--------    def __init__(self,name="qingsword"):        self.name=name    #--------    def __str__(self):        """定制对象返回信息"""        return "Class A Object self.name=%s"%self.name    #间接让repr函数等于str    __repr__=__str__x=A("www.qingsword.com")print(x)#程序输入Class A Object self.name=www.qingsword.com

除了定制对象的输入信息外,如果一个类要用作for循环,Python提供了一个__iter__办法,该办法返回一个迭代对象,可应用for循环迭代这个对象,for会一直调用该对象的__next__()办法拿到循环的下一个值,直到遇到StopIteration谬误(或break)退出循环,能够将一个类定制成可迭代类,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class Fib(object):    #--------    def __init__(self):        self.a,self.b=0,1    #实例自身就是迭代对象,所以iter函数返回本人    def __iter__(self):        return self    #如果对象中的a属性值大于20,就进行循环,否则返回每次循环中失去的a值    def __next__(self):        self.a,self.b=self.b,self.a+self.b        if self.a>20:            raise StopIteration()        return self.ax=Fib()for y in x:    print(y)#这是一个斐波拉契迭代类,程序输入如下11235813#如果删除类中的self.a>20的判断,能够将for循环更改成for y in x:    #能够手动指定一个迭代范畴    if y<20:        print(y)    else:        break

下面的可迭代类看起来更像是一个生成器,但当初咱们还无奈像列表那样应用一个索引来获得单个值,为此python提供了一个__getitem__办法,用于实现获得单个值的类,批改下面的程序如下:

#!/usr/bin/env python#coding=utf-8########class Fib(object):    #依据输出的n,返回x的值    def __getitem__(self,n):        #x和y只是函数中的一个局部变量,每次调用函数,都会被从新设置成1        x,y=1,1        while n>0:            x,y=y,x+y            n-=1        return x         x=Fib()print(x[2])print(x[12])#程序输入2233

如果想要给这个类增加一个切片性能,能够批改下面的代码如下:

#!/usr/bin/env python#coding=utf-8########class Fib(object):    def __getitem__(self,n):        #判断传入的是一个分片类型还是一个索引值        if isinstance(n,int):            x,y=1,1            while n>0:                x,y=y,x+y                n-=1            return x        elif isinstance(n,slice):            #获取分片的起始值            start=n.start            stop=n.stop            if start is None:                start=0            x,y=1,1            L=[]            for a in range(stop):                if a>=start:                    L.append(x)                x,y=y,x+y            return L         x=Fib()print(x[12])print(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/env python#coding=utf-8########class A(object):    def __init__(self):        self._path=""x=A()#调用_path没有问题print(x._path)#但调用一个不存在的sub属性就会报错,提醒sub属性不存在print(x.sub)

python提供了一个__getattr__函数,它接管一个不存在的属性或办法名称,而后返回一个咱们设定的值,如果咱们没有定义返回值,它将返回一个None,这比间接抛出一个谬误要敌对的多,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A(object):    def __init__(self):        self._path=""    def __getattr__(self,attr):        if attr=="blog":            return "www.qingsword.com"        elif attr=="doit":            return "ok"        #一个函数返回值        elif attr=="age":            return lambda :23x=A()print(x.blog)print(x.doit)print(x.age())print(x.nothing)#程序输入www.qingsword.comok23None

请留神__getattr__函数只有当类中没有定义这些属性值时才接管他们,并且依据咱们本人的判断返回值,如果类中有这些属性或办法,则会优先应用它们,另外,如果这个函数接管到了一个没有定义返回值的名称,默认返回None,除非手动抛出一个AttributeError,比方,在if的开端增加上面的代码,再次运行就会抛出一个异样,提醒nothing不存在:

else:raise AttributeError("谬误的属性或办法名:%s"%attr)  

__getattr__最常见的利用就是链式调用,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A(object):    def __init__(self,path=""):        self._path=path        print('self._path is %s' % self._path)    def __getattr__(self,attr):        return A("%s/%s"%(self._path,attr))    def __str__(self):        return self._path    __repr__=__str__x=A()print(x.www.qingsword.com)#程序输入#x=A()初始化时的输入self._path is#x.www时因为没有www属性,调用了__getattr__函数,并且传递了一个www,而后递归调用A,这一步相当于A("/www")self._path is /www#x.www.qingsword会再次激活__getattr__函数,因为www并没有qingsword属性,以此类推self._path is /www/qingswordself._path is /www/qingsword/com/www/qingsword/com

咱们在实例化一个对象的时候,能够通过"对象名.属性名"来应用其中的属性,或者通过"对象名.办法名()"来调用其中的办法,除此之外,python提供了一个__call__函数,这个函数容许对象调用本身,请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A(object):    def __init__(self,name="qingsword"):        self.name=name    def __call__(self):        print("My name is %s"%self.name)x=A()#间接调用对象本身,就相当于执行了对象内的__call__函数,就如同x自身是一个函数而不是类对象一样x()  #输入"My name is qingsword"#咱们能够通过callable()函数来判断一个对象是否可调用,这个函数返回一个布尔值,如果咱们正文掉A类中的call函数,那么上面的第一句就会返回False,大家能够尝试一下print(callable(x))  #Trueprint(callable(str))  #Trueprint(callable([1,2,3]))  #False

0×11.枚举类

Python3.0之后,提供了一个枚举类型,补救了Python2没有枚举的有余,首先来看一个简略的枚举实例:

#!/usr/bin/env python#coding=utf-8#导入枚举模块from enum import Enum#定义一个枚举变量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__) #取得枚举个数for i in range(1,x+1):    print(m(i))#程序输入Month.JanMonth.FebMonth.MarMonth.AprMonth.MayMonth.JunMonth.JulMonth.AugMonth.SepMonth.OctMonth.NovMonth.Dec#获取枚举成员名称和全名,m.__members__.items()会将枚举宰割成一个列表,每个元素都为一个元组,每个元组中又蕴含一个字符串和一个字典类型,宰割后的列表大略是这个样子[("Jan",<Month.Jan:1>),("Feb",<Month.Feb:2>)....],而后应用name接管第一个参数(Jan),member接管第二个参数(<Month.Jan:1>),member能够拜访键,用member.value能够拜访值for name, member in m.__members__.items():    print(name,'=>',member,',',member.value)#程序输入Jan => 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/env python#coding=utf-8from enum import Enum,unique@unique  #unique装璜器能够确保枚举中没有反复值class Week(Enum):    """继承枚举类,能够指定每个名称的值"""    Sun=0 #将起始索引设定成0    Mon=1    Tue=2    Wed=3    Thu=4    Fri=5    Sat=6#拜访办法同枚举print(Week(0))print(Week.Sat.value)print(Week["Thu"])#程序输入Week.Sun6Week.Thu

在Python3中,枚举类常常用于定义常量,因为枚举中每个名称对应的值是不可变的,例如:

#!/usr/bin/env python#coding=utf-8from enum import Enum,unique@uniqueclass const(Enum):    """常量枚举类"""    website="www.qingsword.com"    author="qingsword"    pi=3.1415print(const.website.value)print(const("qingsword"))print(const["pi"].value)#程序输入www.qingsword.comconst.author3.1415

0×12.动静类

动静语言和动态语言在创立类的时候也有所不同,动静语言类的创立是在程序执行后才顺次执行类中的语句,动态化创立一个类,而动态语言是在编译时就曾经创立了这些类;咱们在应用type判断一个类的类型时,往往看到类的类型是type,这又与动静类的创立有什么关系呢?请看上面的实例:

#!/usr/bin/env python#coding=utf-8########class A(object):    def Website(self):        print("www.qingsword.com")x=A()print(type(A))print(type(x))#程序输入,从输入中能够看到,x是A类的一个实例,但A类自身却是一个class type<class 'type'><class '__main__.A'>

实际上type办法不仅能够判断对象的类型,python还能够应用它来动态创建类,而应用type动态创建的类与间接应用class创立的类成果是一样的,这就是为什么下面type判断A类的类型是class type的起因,实际上两者能够看做同一种类型,上面的实例演示了如何通过type动态创建一个类:

#!/usr/bin/env python#coding=utf-8def Website(self):    print("www.qingsword.com")def SayHello(self,name="qingsword"):    print("Hello %s"%name)#B指向一个Type动态创建的类,所以B就代表这个类(A)自身#type的语法:#type("类名",(继承类列表,...),dict(类办法名=绑定的办法名,...))#应用这种办法咱们就能够动静的将内部的办法绑定到一个类中,从而在运行时创立一个类B=type("A",(object,),dict(web=Website,hello=SayHello))x=B()print(x)print(type(B))x.web()x.hello("xxx")#程序输入<__main__.A object at 0x7f7d70821048><class 'type'>www.qingsword.comHello xxx