博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
36-高级特性之自定义类(1)
阅读量:5134 次
发布时间:2019-06-13

本文共 7388 字,大约阅读时间需要 24 分钟。

2. 自定义类:

name 说明 触发时机及功能
__slots__() 限制本类的instance的所有属性(以tuple的形式写死了,不能再定义更多的属性),但无法限制其子类 定义实例属性时
__len__() 类似统计元素个数 len(实例名)
__iter__() 将普通实例变成Iterable对象 for 迭代某个Iterable对象时
__next__() 逐个取Iterable对象的单个元素 for 迭代某个Iterable对象; next(对象名)
__getitem__() 让对象具有下标操作(类比集合的下标操作) 对象名[]
__setitem__()
__delitem()__
__getattr__() 外部调用一个当前对象不存在的属性(或方法),返回一个属性(或函数) 当外部引用了一个当前对象不存在的属性或方法时
__call()__ 让对象变成callable对象,即类似函数 对象名()
#使用__xxx__定制类#__slots__:限制本类{无法作用于其子类}的所有instance的属性集合#比如:__slots__ = ('name','age'),在class的定义中设置,以后该class的所有instance只能有这两个attribution#__len__:'''如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。要让len()函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。例如,我们写一个 Students 类,把名字传进去:class Students(object):    def __init__(self, *args):        self.names = args  #显然args是可变参数,可以传入多个Name,self.names相当于一个list    def __len__(self):        return len(self.names) #返回names的len只要正确实现了__len__()方法,就可以用len()函数返回Students实例的“长度”:'''#1.__str__():当使用print()一个class的一个instance时,会默认调用__str__(),我们可以自定义这个方法#__repr__():当直接在控制台输入这个instance变量名时{为调试服务},会默认执行__repr__(),我们也可以自定义它#偷懒的小技巧:先自定义__str__(),再直接__repr__ = __str__,即可实现两者统一,class Student_1(object):    def __init__(self,name):        self.__name = nameS1 = Student_1('Willian')print(S1,'\n')class Student_2(object):    def __init__(self,name):        self.__name = name    def __str__(self):        return ('Student_2 object:%s' % (self.__name))    __repr__ = __str__S1 = Student_2('Willian')print(S1,'\n')##########################################################################################2.__iter__():对一个类自定义一个__iter__()方法可以使它的所有对象变成Iterable#再定义一个__next__()方法可以,通过循环,不断调用next(对象名)返回一些东西,直至遇到StopIteration退出循环class Fib(object):    def __init__(self):        self.a,self.b = 0,1 #为了书写方便,暂时不写私有属性。这里是初始化计数器    def __iter__(self):        return self #返回一个Iterable对象    def __next__(self): #待会儿循环会不断调用一个对象的这个next()        self.a,self.b = self.b,(self.a+self.b)        if (self.a > 10000):            raise StopIteration() #当主调方得到StopIteration,循环停止        return self.a #当前的fib(i),i从1开始:1,1,2F1 = Fib()for n in F1: #以后不用多写F1 = Fib(),直接写 for n in Fib() #因为它这里只需要使用一个对象    print(n) #这个n即fib(i)print('\n')#首先,F1= Fib()时,系统执行了__iter__(),返回的F1变成了一个Iterable#此处通过for 迭代F1,底层其实是调用__next__()去了,之后就得到了fib(i)##########################################################################################3.__getitem__(): 让上面那个Fib()不仅是Iterable,还支持Fib()[]{即支持下标操作},更像list了#Fib()[1] #发现它不支持indexingclass Fib_1(object):    def __getitem__(self,n): #当使用 Fib_1()[i]时,就会激发这个方法        a,b = 0,1        for x in range(n): #这个方法很笨,也许可以用generator优化,节省时间和空间            a,b = b,(a+b)        return aF2 = Fib_1()for i in range(1,10):    print(F2[i])print(Fib_1()[20],'\n') #本来Fib_1()就是个对象,只是赋给F2而已,两者本来就等价#继续优化__getitem__(),让他支持list的切片,让Fib()更像list:class Fib_2(object):    def __getitem__(self,n): #对传入的index即n做多分支的处理:int,slice        if isinstance(n,int): #如果n是int,说明只是Fib()[n]操作            a,b = 0,1            for x in range(n): #循环n次                a,b = b,(a+b)            return a        if isinstance(n,slice): #n是一个切片,要做截断和append出一个list返回            start = n.start            stop = n.stop   #切片本身也是[lo,hi),这个左闭右开区间的特点存在于python的所有地方            if start is None:                start = 0            L = [] #初始化L            a,b = 0,1            for x in range(stop):                if (x >= start): #x处于[start,stop),是切片的一部分,就把当前的a追加到L                    L.append(a)                a,b = b,(a+b)            return L #返回一个listF3 = Fib_2()print(F3[5],'\n')print(F3[1:6],'\n') #暂时只是初步支持切片,更加强大的支持切片还需要对__getitem__()做改造#与__getitem__()类似,可实现__setitem__()和__delitem__(),让我们自定义的class的instance#可以具备和官方的list,tuple,dict,等等类型一样强大的功能。#这一切都得益于python的“鸭子类型”:通过定义函数支持“不是鸭子的类型长得像鸭子”##########################################################################################4.__getattr__():在自定义的class中添加__getattr__(),当外部调用一个class不存在的属性,#会激发这个方法,它会动态返回一个属性,或者函数{lambda也暂且认定为一个函数}class Stu_1(object):    def __init__(self,name):        self.name = names1 = Stu_1('hao')#print(s1.score,'\n')class Stu_2(object):    def __init__(self,name):        self.name = name    def __getattr__(self,attr): #attr即为我们想要外部调用的“不存在属性或者方法”        if (attr == 'score'):             return 99 #访问了不存在的属性:返回99分        if (attr == 'age'):            return lambda : 23#调用了不存在的方法,返回lambda表达式或者函数名            s2 = Stu_2('hao')print(s2.score,'\n')print(s2.age(),'\n') #等价于外部调用了一个不存在的方法,__getattr__()也能处理##########################################################################################5.__call__():调用instance本身会激发此方法:比如s3 = Stu_3(),若执行s3('hao'),#等价于Stu_3()('hao'),{一般是调用s3的属性和方法:s3.name, s3.age()}class Stu_3(object):    def __init__(self,name):        self.name = name    def __call__(self,age): #age是调用instance本身时,传入的参数        print("name:%s, age:%d" % (self.name, age))s3 = Stu_3('haozhang')s3(23)print('\n')#使用callable()查看一个对象是否可以调用:即s3()这种用法print(callable(Stu_1), callable(Stu_2), callable(Stu_3),'\n') #结果是instance默认都有__call__(),都可以调用print(callable(abs), callable(max),'\n') #都可以print(callable([1,2,3]), callable('123456'), callable({}), '\n') #都不可以###########################################################################################6.链式调用:class Person(object):    def name(self,name):        self.name = name        return self    def age(self,age):        self.age = age        return self    def show(self):        print("my name is %s, my age is %d" % (self.name,self.age))P1 = Person()P1.name('haozhang').age(23).show() #注意这里全程只用到了一个对象P1,只是不停地被return和执行调用函数#首先调用name(),完成self.name赋值后返回P1这个对象,再用P1来调用age,之后返回P1,再来调用showprint('\n')##########################################################################################7.结合__init__(),__getattr__(),__call__(),以及链式调用,写一个支持RestAPI架构的API类class Chain(object):    def __init__(self,path=''): #允许传入参数path,但默认参数为空,所以等价于:可传可不传        self.__path = path #调用完__init__()后,系统会自动返回一个instance给主调方那里    def __getattr__(self,path):        return Chain('%s/%s' % (self.__path,path))    #在这儿,必须用户,手动创建并返回,一个instance给主调用方。        #等价于手动创建了一个对象Chain(),并传入一个新的path,其内容是:        #{当前self.__path和path的字符拼接,二者中间/连接},这个Chain()会将这个新path        #传入新的__init__()方法。之后循环迭代下去,直至链式调用停止    def __call__(self,attr):        return Chain('%s/%s' % (self.__path,attr))        #其实可以复用:在外部写一句:__call__ = __getattr__,因为两者代码是一样的    def __str__(self): #把这个调用过程打印出来,激发时机:当执行print(调用)或者控制台输出“链式调用”        return self.__path #因为整个本质上一直在拼接一个字符串给__path    __repr__ = __str__ #让控制台可以直接输出 最终的self.__path {这句不可省略}C1 = Chain() #默认参数写了'',这里可以不写初始的pathprint(C1.status.user.timeline.list)print('\n')print(C1.users('Willian').repos)print('\n')#Chain().users('Willian').repos看做是:Chain()对象调用uesrs属性,        #然后返回对象I,I再调用对象自身,并带参'Willian',即I('Willian')        #之后再返回对象。再之后再调用属性repos{这次又要回到__getattr__()}        #网友优化版:__getattr__和__call__不是重复创建类,而是不断return原来的类,较小内存消耗class Chain_2(object):    def __init__(self,path=''):        self.__path = path    def __getattr__(self,path):        self.__path = ('%s/%s' % (self.__path, path))        return self    def __str__(self):        return self.__path    __call__ = __getattr__ #代码逻辑是相同的,直接复用代码C2 = Chain_2()print(C2.status.user.timeline.list)print('\n')print(C2.users('Willian').repos) #这里出问题了,因为C2一直没变过,此时应该换个新对象C3了print('\n')C3 = Chain_2()print(C3.users('Willian').repos) #这里出问题了,因为C2一直没变过,此时应该换个新对象C3了print('\n')            #########################################################################################

转载于:https://www.cnblogs.com/LS1314/p/8504564.html

你可能感兴趣的文章
前端性能优化之重排和重绘
查看>>
Assets和Raw区别
查看>>
【luogu4185】 [USACO18JAN]MooTube [并查集]
查看>>
手机号脱敏处理
查看>>
CI控制器调用内部方法并载入相应模板的做法
查看>>
Hdu - 1002 - A + B Problem II
查看>>
HDU - 2609 - How many
查看>>
每天CookBook之Python-003
查看>>
每天CookBook之Python-004
查看>>
Android设置Gmail邮箱
查看>>
StringBuffer的用法
查看>>
js编写时间选择框
查看>>
PHP压缩文件操作
查看>>
4.你认为一些军事方面的软件系统采用什么样的开发模型比较合适?
查看>>
日常开发需要掌握的Maven知识
查看>>
Java数据结构和算法(四)--链表
查看>>
JIRA
查看>>
ssl介绍以及双向认证和单向认证原理
查看>>
【BZOJ2441】【中山市选2011】小W的问题(树状数组+权值线段树)
查看>>
小技巧——直接在目录中输入cmd然后就打开cmd命令窗口
查看>>