Python-面向对象(进阶)
一、什么是反射
反射的概念是Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于用于反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
二、四个可以实现自省的函数(下列方法适用于类和对象)
1、hasattr(object,name)
判断object中有没有一个name字符串对象的方法或属性
2、getattr(object,name,defaullt=None)
获取属性
3、setattr(s,y,v)
设置属性
4、delattr(x,y)
删除属性
1 class BlackMedium: 2 feature='Ugly' 3 def __init__(self,name,addr): 4 self.name=name 5 self.addr=addr 6 7 def sell_house(self): 8 print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) 9 def rent_house(self): 10 print('%s 黑中介租房子啦,傻逼才租呢' %self.name) 11 12 b1=BlackMedium('万成置地','回龙观天露园') 13 14 #检测是否含有某属性 15 print(hasattr(b1,'name')) 16 print(hasattr(b1,'sell_house')) 17 18 #获取属性 19 n=getattr(b1,'name') 20 print(n) 21 func=getattr(b1,'rent_house') 22 func() 23 24 # getattr(b1,'aaaaaaaa') #报错 25 print(getattr(b1,'aaaaaaaa','不存在啊')) 26 27 #设置属性 28 setattr(b1,'sb',True) 29 setattr(b1,'show_name',lambda self:self.name+'sb') 30 print(b1.__dict__) 31 print(b1.show_name(b1)) 32 33 #删除属性 34 delattr(b1,'addr') 35 delattr(b1,'show_name') 36 #delattr(b1,'show_name111')#不存在,则报错 37 38 print(b1.__dict__)
1 True 2 True 3 万成置地 4 万成置地 黑中介租房子啦,傻逼才租呢 5 不存在啊 6 {'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x005DF0C0>} 7 万成置地sb 8 {'name': '万成置地', 'sb': True}
总结:
hasattr(obj,'属性') #obj.属性 是否存在 存在返回True
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性
三、为什么要用反射:
反射的好处及时:可以事先定义好街口,街口只有在被完成后才会真正执行,这事先额即插即用,这其实是一种后期绑定,即你可以事先把主要的逻辑谢写好(之定义接口),然后后期再去实现接口功能。
示例:
1 class FtpClient: # 程序员A 2 'ftp客户端,但是还么有实现具体的功能' 3 def __init__(self,addr): 4 print('正在连接服务器[%s]' %addr) 5 self.addr=addr
1 from module import FtpClient 2 f1=FtpClient('192.168.1.1') #程序员B 3 if hasattr(f1,'get'): 4 func_get=getattr(f1,'get') 5 func_get() 6 else: 7 print('---->不存在此方法') 8 print('处理其他的逻辑') 9 10 不影响alex的代码编写
四、动态导入模块
1、创建一个:m1文件夹,再它下面创建一个t.py文件
1 print('---------------->') 2 def test1(): 3 print('test1') 4 5 def _test2(): 6 print('test2')
2、再新建一个a.py的文件
1 module_t=__import__('m1.t') # 这种方式,导入的都是m1文件夹。 2 print(module_t) 3 module_t.t.test1() # 所以调用模块的方法,必须写全那个模块 4 # from m1.t import * # 有私有模块,这样是不能调用的,必须写出模块的名字 5 # from m1.t import test,_test2 6 7 import importlib 8 m=importlib.import_module('m1.t') # 这种直接就是调用m1下的t.py模块 9 print(m) 10 m.test1() 11 m._test2()
结果:
1 ----------------> 2 <module 'm1' (namespace)> 3 test1 4 <module 'm1.t' from 'D:\\python\\day26\\m1\\t.py'> 5 test1 6 test2
五、三个参数,给对象添加属性
__setattr__ :添加/修改属性会触发它的执行
__delattr__ :删除属性的时候回触发
__getattr__ :只有在使用点调用属性且属性不存在的时候才会触发
作用:系统内置函数属性(如果不自己定义,就用系统默认定义的)
综合应用示例:
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __getattr__(self, item): 7 print('----> from getattr:你找的属性不存在') 8 9 10 def __setattr__(self, key, value): 11 print('----> from setattr') 12 # self.key=value #这就无限递归了,你好好想想 13 # self.__dict__[key]=value #应该使用它 14 15 def __delattr__(self, item): 16 print('----> from delattr') 17 # del self.item #无限递归了 18 self.__dict__.pop(item) 19 20 #__setattr__添加/修改属性会触发它的执行 21 f1=Foo(10) 22 print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 23 f1.z=3 24 print(f1.__dict__) 25 26 #__delattr__删除属性的时候会触发 27 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 28 del f1.a 29 print(f1.__dict__) 30 31 #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 32 f1.xxxxxx
分开使用:
示例:__getattr__(常用,重点)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __getattr__(self, item): 7 print('执行__getattr__') 8 9 f1=Foo(10) 10 print(f1.y) 11 12 #没有的时候就会触发: __getattr__ 13 print(getattr(f1,'y')) #len(str)---->str.__len__() 14 f1.ssssssssssssssssssssssssssssss
结果:
1 10 2 10 3 执行__getattr__
示例2:__delattr__(不常用)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __delattr__(self, item): 7 print('删除操作__delattr__') 8 9 f1=Foo(10) 10 del f1.y 11 del f1.x
示例3: __setattr__(不常用)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __setattr__(self,key,value): 7 print('__setattr__执行') 8 #self.key=value 9 self.__dict__[key]=value 10 11 #增加键、值到字典中 12 f1=Foo(10) 13 print(f1.__dict__) 14 15 f1.z=2 16 print(f1.__dict__)
总结:
obj点的方式去操作属性时触发的方法
__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发
六、二次加工标准类型(包装)
包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实很多场景下我们都需要基于标砖数据类型来定制我们自己的数据类型,新增/改写方法,这就是用到了我们刚刚学的继承/派生知识(其他的标准类型均可以他通过下面的方式进行二次加工)
1 class List(list): # 继承了list 父类 2 def append(self, p_object): 3 '我改写的方法' 4 if not isinstance(p_object,str): 5 print('只有字符串类型能被添加到列表中') 6 return 7 # self.append(p_object) #进入无限递归 8 super().append(p_object) 9 def show_mid(self): 10 '我新增的方法' 11 index=int(len(self)/2) 12 print(self[index]) 13 14 15 l1=List('hello') 16 17 l1.append('abc') 18 print(l1,type(l1)) 19 20 #数字无法添加成功 21 l1.append(1) 22 print('-->',l1) 23 24 #基于标准类型新增了功能 25 l1.show_mid()
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
1、通过触发__getattr__方法,找到read方法
示例:
1 import time 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 self.file=open(filename,mode,encoding=encoding) # 通过原方法获取文件句柄 5 self.mode=mode 6 self.encoding=encoding 7 8 def __getattr__(self, item): 9 print(item,type(item)) 10 self.file.read #self.file里面有read方法 11 return getattr(self.file,item) #能过字符串来找到,并通过return返回,就找到了read方法, 12 13 f1=FileHandle('a.txt','r') 14 print(f1.file) 15 print(f1.__dict__) #类的字典里,没有read方法,就触发了__getattr__方法 16 print(f1.read) #找到了read方法 17 18 sys_f=open('b.txt','w+') 19 print('---->',getattr(sys_f,'read')) #找到了read方法
结果:
1 <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'> 2 3 read <class 'str'> 4 5 <built-in method read of _io.TextIOWrapper object at 0x01638EB0> 6 7 {'encoding': 'utf-8', 'file': <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>, 'mode': 'r'} 8 9 ----> <built-in method read of _io.TextIOWrapper object at 0x01638F30>
2、往文件里面写入内容
示例:
1 import time 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 # self.filename=filename 5 self.file=open(filename,mode,encoding=encoding) 6 self.mode=mode 7 self.encoding=encoding 8 def write(self,line): 9 print('------------>',line) 10 t=time.strftime('%Y-%m-%d %X') 11 self.file.write('%s %s' %(t,line)) 12 13 def __getattr__(self, item): 14 # print(item,type(item)) 15 # self.file.read 16 return getattr(self.file,item) 17 18 f1=FileHandle('a.txt','w+') 19 f1.write('1111111111111111\n') 20 f1.write('cpu负载过高\n') 21 f1.write('内存剩余不足\n') 22 f1.write('硬盘剩余不足\n')
执行结果:
会创建一个a.txt的文件,并往里面写入内容:
1 2016-12-23 18:34:16 1111111111111111 2 2016-12-23 18:34:16 cpu负载过高 3 2016-12-23 18:34:16 内存剩余不足 4 2016-12-23 18:34:16 硬盘剩余不足
七、isinstance(obj,cls) 和 issubclass(sub,super)
1、isinstance(obj,cls) 检查是否obj 是否是类cls的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 print(isinstance(obj,Foo)) # True
2、issubclass(sub,super) 检查sub类是否是supper类的派生类(继承)
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 print(issubclass(Bar,Foo)) # True
八、__getattribute__
示例1:不存在的属性访问,就会触发__getattr__方法
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('执行的是我') 7 #return self.__idct__[item] 8 9 f1=Foo(10) 10 print(f1.x) 11 f1.xxxxxx #不存在的属性访问,触发__getattr__
执行结果:
1 10 2 执行的是我
示例2:不管是否存在,都会执行__getattribute__方法
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattribute__(self, item): 6 print('不管是否存在,我都会执行') 7 8 f1=Foo(10) 9 f1.x 10 f1.xxxxxxx 11 12 13 ---------------------------------- 14 不管是否存在,我都会执行 15 不管是否存在,我都会执行
示例3:
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): #相当于监听大哥的异常,大哥抛出导常,他就会接收。-----item就是调用的属性名,x xxxxxxx 6 print('执行的是我') 7 # return self.__dict__[item] 8 9 def __getattribute__(self, item): 10 print('不管是否存在,我都会执行') 11 raise AttributeError('抛出异常了') 12 13 f1=Foo(10) 14 f1.x #结果是:10 ,调用会触发系统的 15 f1.xxxxxxx #如果不存在会触发自己定义的 16 17 --------------------------------------------- 18 不管是否存在,我都会执行 19 执行的是我 20 不管是否存在,我都会执行 21 执行的是我
九、__setitem__, __getitem__, __deelitem__ (操作字典就用item的方式)
obj['属性'] 的方式去操作属性时触发的方法
__getitem__:obj['属性'] 时触发
__setitem__ :obj['属性'] = 属性值 时触发
__delitem__ :del obj['属性'] 时触发
示例:
1 class Foo: 2 3 def __getitem__(self, item): 4 print('getitem', item) 5 return self.__dict__[item] 6 7 def __setitem__(self, key, value): 8 print('setitem') 9 self.__dict__[key] = value 10 11 def __delitem__(self, key): 12 print('delitem') 13 self.__dict__.pop(key) 14 15 16 f1 = Foo() 17 print(f1.__dict__) 18 f1['name'] = 'agon' #setitem 19 f1['age'] = 18 20 print('====>', f1.__dict__) 21 22 del f1['name'] # delitem 23 print(f1.__dict__) 24 25 print(f1['age']) # 调用getitem 26 27 28 29 --------------------------------------------- 30 {} 31 setitem 32 setitem 33 ====> {'name': 'agon', 'age': 18} 34 delitem 35 {'age': 18} 36 getitem age 37 18
示例2:
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 5 def __getitem__(self, item): 6 print(self.__dict__[item]) 7 8 def __setitem__(self, key, value): 9 self.__dict__[key]=value 10 def __delitem__(self, key): 11 print('del obj[key]时,我执行') 12 self.__dict__.pop(key) 13 def __delattr__(self, item): 14 print('del obj.key时,我执行') 15 self.__dict__.pop(item) 16 17 f1=Foo('sb') # 实例化 18 f1['age']=18 # 添加属性age 19 f1['age1']=19 20 print(f1.__dict__) 21 22 del f1.age1 # 不会触发delitem,触发delattr 23 print(f1.__dict__) 24 25 del f1['age'] # 删除属性age 26 f1['name']='alex' 27 print(f1.__dict__) 28 29 30 31 32 ---------------------------- 33 34 {'name': 'sb', 'age': 18, 'age1': 19} 35 del obj.key时,我执行 36 {'name': 'sb', 'age': 18} 37 del obj[key]时,我执行 38 {'name': 'alex'}
十、__str__ __repr__ __format__
1、改变对象的字符串显示__str__ __repr__(只能是字符串的值,不能是非字符串的值)
示例1:
1 l = list('hello') 2 print(1) 3 4 file=open('test.txt','w') 5 print(file) 6 7 ----------------------------- 8 1 9 <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
示例2:__str__ 方法
1 #自制str方法 2 class Foo: 3 def __init__(self,name,age): 4 self.name=name 5 self.age=age 6 7 def __str__(self): 8 return '名字是%s 年龄是%s' %(self.name,self.age) 9 10 f1=Foo('age',18) 11 print(f1) #背后实际上是 触发:str(f1)---->f1.__str__(),现在是覆盖了__str__()方法 12 13 x=str(f1) 14 print(x) 15 16 y=f1.__str__() 17 print(y) 18 19 20 ---------------------------- 21 22 名字是age 年龄是18 23 名字是age 年龄是18 24 名字是age 年龄是18
示例3: __repr__方法
1 #触发__repr__方法,用在解释器里输出 2 class Foo: 3 def __init__(self,name,age): 4 self.name=name 5 self.age=age 6 7 def __repr__(self): 8 return '名字是%s 年龄是%s' %(self.name,self.age) 9 10 f1=Foo('agon',19) 11 print(f1) 12 13 --------- 14 名字是agon 年龄是19
示例4:__str__ __repr__ 共存
1 # 当str与repr共存时 2 class Foo: 3 def __init__(self,name,age): 4 self.name=name 5 self.age=age 6 7 def __str__(self): 8 return '名字是%s + 年龄是%s' % (self.name,self.age) 9 #return 'a' 10 def __repr__(self): 11 return '名字是%s 年龄是%s' %(self.name,self.age) 12 13 f1=Foo('egon',19) 14 #repr(f1)--->f1.__repr__() 15 print(f1) #str(f1)--->f1.__str__()---->f1.__repr__()
总结:
str函数或者print函数 调用的是obj.__str__()
repr或者交互式解释器 调用的是obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来替代输出
注意:这两方法的返回值必须是字符串,否则抛出异常
2、自定制格式化字符串__format__
format的用法:
示例1: 博客: http://www.cnblogs.com/JerryZao/p/8604102.html
1 x = '{0}{0}{0}'.format('dog') 2 print(x) # dogdogdog
不用__format__的方式实现
示例2:
1 class Date: 2 def __init__(self,year,mon,day): 3 self.year=year 4 self.mon=mon 5 self.day=day 6 7 d1=Date(2016,12,26) 8 print(d1) 9 10 x = '{0.year}{0.mon}{0.day}'.format(d1) # 0 代表di对象,0.year就是调用属性 11 y = '{0.year}:{0.mon}:{0.day}'.format(d1) 12 z = '{0.year}-{0.mon}-{0.day}'.format(d1) 13 print(x) 14 print(y) 15 print(z) 16 17 --------------------------------------------------- 18 19 <__main__.Date object at 0x0041F810> 20 20161226 21 2016:12:26 22 2016-12-26
用__format__的方式实现
示例3:
1 format_dic={ #定义一个格式字典 2 'ymd':'{0.year}:{0.month}:{0.day}', 3 'm-d-y':'{0.month}-{0.day}-{0.year}', 4 'y:m:d':'{0.year}:{0.month}:{0.day}', 5 } 6 7 class Date: 8 def __init__(self,year,month,day): 9 self.year=year 10 self.month=month 11 self.day=day 12 13 def __format__(self, format_spec): 14 print('我执行啦') 15 print('----->',format_spec) 16 if not format_spec or format_spec not in format_dic: 17 format_spec='ymd' 18 fmt=format_dic[format_spec] 19 return fmt.format(self) # 相当于 '{0.year}:{0.month}:{0.day}'.format(d1) 20 21 d1=Date(2016,12,29) 22 # print(format(d1)) #d1.__format__() 23 # print(format(d1)) 24 25 print(format(d1,'ymd')) 26 print(format(d1,'y:m:d')) 27 print(format(d1,'m-d-y')) 28 print(format(d1,'m-d:y')) 29 print('===========>',format(d1,'sdsdddsfdsfdsfdsfdsfsdfdsfsdfds')) 30 31 32 ---------------------- 33 我执行啦 34 -----> ymd 35 2016:12:29 36 我执行啦 37 -----> y:m:d 38 2016:12:29 39 我执行啦 40 -----> m-d-y 41 12-29-2016 42 我执行啦 43 -----> m-d:y 44 2016:12:29 45 我执行啦 46 -----> sdsdddsfdsfdsfdsfdsfsdfdsfsdfds 47 ===========> 2016:12:29
十一:__slots__ (慎用)
1、__slots__ 是一个变量,变量值可以是列表,元组,或者可迭代对象,可以是一个字符串(意味着所有实例只有一个数据属性)。
2、引子:使用点来访问属性本事就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的属性时独立的)。
3、为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__当你定义__slots__后,__solts__就会为实例使用一种更加紧凑的内部标识。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用__slots__中定义的那些属性名。
4、注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象。关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
__slots__的作用:节省内存空间
1:一个key的情况
示例1:
1 #__slots__ (作用:就是节省内存) 2 #一个key的值 3 4 class Foo: 5 __slots__='name' #定义在类中的类变量,由这个类产生的实例,不在具有__dict__的属性字典,限制了创建属性 6 7 f1=Foo() 8 f1.name='agon' 9 print(f1.name) #只能有一个name属性 10 print(Foo.__slots__) 11 print(f1.__slots__) 12 13 14 ---------------------------- 15 agon 16 name 17 name
2、两个key的情况:
示例2:
1 #两个key的情况 2 class Foo: 3 __slots__=['name','age'] 4 5 f1=Foo() 6 7 print(Foo.__slots__) 8 print(f1.__slots__) 9 10 f1.name='egon' 11 f1.age=17 12 13 print(f1.name) 14 print(f1.age) 15 # f1.gender='male' #会报错,加不上,#AttributeError: 'Foo' object has no attribute 'gender' 16 17 #只能定义__slots__提供的属性(这里就定义了两个属性)大家都用一个属性字典,优势就是节省内存 18 f2=Foo() 19 print(f2.__slots__) 20 f2.name='alex' 21 f2.age=18 22 print(f2.name) 23 print(f2.age) 24 25 26 ---------------------------------------------------- 27 28 ['name', 'age'] 29 ['name', 'age'] 30 egon 31 17 32 ['name', 'age'] 33 alex 34 18
十二、__doc__
1、类的描述信息
示例:
1 class Foo: 2 '我是描述信息' 3 pass 4 5 print(Foo.__doc__)
2、该属性无法继承(每个类默认都会提供)
#__doc__ 该属性无法继承 class Foo: pass class Bar(Foo): pass print(Foo.__dict__) #只要加上了__doc__,该属性就无法继承给子类 print(Bar.__dict__) #原理就是在底层字典里面,会加一个'__doc__': None,
十三、__module__和__class__
__module__ 表示当前操作的对象在哪个模块
__class__ 表示当前操作的对象的类是什么
1、创建lib/aa.py
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class C: 5 6 def __init__(self): 7 self.name = ‘SB' 8
2、出书模块 和 输出类
1 from lib.aa import C 2 3 obj = C() 4 print obj.__module__ #输出 lib.aa,即:输出模块,obj来自哪个模块 5 print obj.__class__ #输出 lib.aa.C,即:输出类,obj来自哪个类
十四、__del__析构方法(垃圾回收时自动触发)
析构方法:当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 def __del__(self): 5 print('我执行啦') 6 7 f1=Foo('alex') 8 9 #del f1 #删除实例会触发__del__ 10 del f1.name #删除实例的属性不会触发__del__ 11 print('---------------->') 12 13 14 15 1 ----------------> 16 2 我执行啦
十五、__call__
对象后面加上括号,触发执行。
注:构造方法的执行是由对象触发的,即:对象=类名() 而对于__call__方法的执行是由对象后加括号触发的,即:类()() 或者对象()
1 class Foo: 2 3 def __init__(self): 4 pass 5 6 def __call__(self, *args, **kwargs): 7 print('__call__') 8 print(self) 9 print(args) 10 print(kwargs) 11 12 13 14 obj = Foo() # 执行 __init__ 15 print(obj) 16 obj([2,3,4],s=1) # 执行 __call__ 17 18 19 -------------------------------------------- 20 <__main__.Foo object at 0x0024F870> 21 __call__ 22 <__main__.Foo object at 0x0024F870> 23 ([2, 3, 4],) 24 {'s': 1}
十六、__next__ 和__iter__ 实现迭代器协议
一、什么是迭代器协议
1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
二、python中强大的for循环
for循环的本质:循环所有对象,全都是使用迭代器协议。
(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环式,调用了他们内部的__iter__方法,把他们变成了可迭代对象
然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉StopIteration异常,以终止迭代。
示例1:
1 class Foo: 2 def __init__(self,n): 3 self.n=n 4 5 def __iter__(self): #把一个对象就成一个可迭代对象,必须有__iter__ 6 return self 7 8 def __next__(self): 9 self.n+=1 10 return self.n 11 12 f1=Foo(10) 13 # for i in f1: #for循环本质就是调用他:f1.__iter__() == iter(f1) 14 # print(i) 15 16 print(f1.__next__()) 17 print(next(f1)) 18 print(next(f1)) 19 print(next(f1)) 20 print(next(f1)) 21 print(next(f1)) 22 23 24 for i in f1: #for循环本质就是调用他:f1.__iter__() == iter(f1) 25 print(i) 26 27 -------------------------------------------------- 28 29 11 30 12 31 13 32 14 33 15 34 16 35 17 36 18 37 19 38 20 39 21 40 22 41 23 42 24 43 25 44 26 45 会一直无限循环下去..... 46 下面部分省略.....
示例2:
1 class Foo: 2 def __init__(self,n): 3 self.n=n 4 5 def __iter__(self): #把一个对象就成一个可迭代对象,必须有__iter__ 6 return self 7 8 def __next__(self): 9 if self.n == 13: 10 raise StopIteration('终止了') 11 self.n+=1 12 return self.n 13 14 15 f1=Foo(10) 16 17 # print(f1.__next__()) 18 # print(f1.__next__()) 19 # print(f1.__next__()) 20 # print(f1.__next__()) 21 22 for i in f1: #f1.__iter__() == iter(f1) 23 print(i) #obj.__next__() 24 25 ------------------------------------------------------ 26 11 27 12 28 13
三、菲波那切数列
用迭代器协议的方法实现:一次产生一个值
1 #斐波那契数列 2 class Fib: 3 def __init__(self): 4 self._a=1 5 self._b=1 6 7 def __iter__(self): 8 return self 9 def __next__(self): 10 if self._a > 100: 11 raise StopIteration('终止了') # >100 就抛出异常 12 self._a, self._b = self._b, self._a + self._b #1+1=b; a,b=b,a(等于交换值) 13 return self._a 14 15 f1=Fib() 16 print(next(f1)) 17 print(next(f1)) 18 print(next(f1)) 19 print(next(f1)) 20 print(next(f1)) 21 print('==================================')# 从上次next的位置继续 22 for i in f1: 23 print(i) 24 25 26 ---------------------------------- 27 1 28 2 29 3 30 5 31 8 32 ================================== 33 13 34 21 35 34 36 55 37 89 38 144
十七、描述符(__get__ __set__ __delete__)(新式类中描述符在大型开发中常用,必须掌握)
描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__() __set__() __delete__() 三个方法中的一个,这也被称为描述符协议。
1 class 描述符: 2 def __get__(): 3 pass 4 def __set__(): 5 pass 6 def __delete__(): 7 pass 8 9 class 类: 10 name=描述符() 11 12 obj=类() 13 obj.name #get方法 14 obj.name='egon' #set方法 15 del obj.name #delete
描述符的三种方法:
__get__(): .调用一个属性时,触发
__set__(): .为一个属性赋值时,触发
__delete__(): 采用del.删除属性时,触发
1、定义一个描述符
示例1:
1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 2 def __get__(self,instance,owner): 3 print('get方法') 4 def __set__(self, instance, value): 5 print('set方法') 6 def __delete__(self, instance): 7 print('delete方法')
2、描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
1 class Foo: 2 def __get__(self,instance,owner): 3 print('===>get方法') 4 def __set__(self, instance, value): 5 print('===>set方法') 6 def __delete__(self, instance): 7 print('===>delete方法') 8 9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法 10 f1=Foo() 11 f1.name='egon' 12 print(f1.name) 13 del f1.name 14 #疑问:何时,何地,会触发这三个方法的执行
3、描述符应用之何时?何地?
示例3:
1 #描述符应用之何时?何地? 2 #描述符Str 3 class Foo: 4 def __get__(self,instance,owner): 5 print('===>get方法') 6 def __set__(self, instance, value): 7 print('===>set方法') 8 def __delete__(self, instance): 9 print('===>delete方法') 10 11 class Bar: 12 x=Foo() #在何地? 13 14 # print(Bar.__dict__)#在何时? 15 16 b1=Bar() 17 b1.x #调用就会触发上面的get方法 18 b1.x=1 #赋值 19 del b1.x 20 21 print(b1.x) #触发了描述器里面的get方法,得到None 22 b1.x=1 #触发了描述器里面的set方法,得到{} 23 print(b1.__dict__) #写到b1的属性字典中 24 del b1.x #打印===>delete方法 25 26 --------------------------------------------------------- 27 ===>get方法 28 ===>set方法 29 ===>delete方法 30 ===>get方法 31 None 32 ===>set方法 33 {} 34 ===>delete方法
4、描述符分为两种
①、数据描述符:至少实现了__get__() 和 __set__()两种方法
示例:
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get')
②、非数据描述符:没有实现__set__()方法
示例:
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
5、注意事项:
① 描述符本身应该定义成新式类,被代理的类也应该是新式类
② 必须把描述符定义成另外一个雷触发的属性类,不能定义到构造函数中 init call
1 class Foo: 2 def __get__(self, instance, owner): 3 print('===>get方法') 4 print('+++', self) # Foo的对象 x 5 print('---', instance) # Bar的对象 b1 6 print('===', owner) # Bar类 7 def __set__(self, instance, value): 8 print('===>set方法') 9 print('---', instance) # Bar的对象 10 print('===', value) # 设置的值 11 def __delete__(self, instance): 12 print('===>delete方法') 13 print(self) # Foo的对象 14 print(instance) 15 16 class Bar: 17 x=Foo() #定义一个描述符 18 def __init__(self,n): 19 self.x=n 20 21 b1=Bar(10) #触发set方法 22 # print(b1) 23 # print(b1.__dict__) 24 print('-------------------------------------------') 25 b1.x 26 print('----------------------------------------------') 27 del b1.x 28 29 30 31 ------------------------------------------------------------------------------------ 32 33 ===>set方法 34 --- <__main__.Bar object at 0x01E3AB90> 35 === 10 36 ------------------------------------------- 37 ===>get方法 38 +++ <__main__.Foo object at 0x005ACF30> 39 --- <__main__.Bar object at 0x01E3AB90> 40 === <class '__main__.Bar'> 41 ---------------------------------------------- 42 ===>delete方法 43 <__main__.Foo object at 0x005ACF30> 44 <__main__.Bar object at 0x01E3AB90>
三、要严格遵循该优先级,优先级由高到底分别是:
1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、找不到的属性触发__getattr__()
类属性>数据描述符
示例:
1 class Foo: 2 def __get__(self,instance,owner): 3 print('===>get方法') 4 def __set__(self, instance, value): 5 print('===>set方法') 6 def __delete__(self, instance): 7 print('===>delete方法') 8 9 class Bar: 10 x=Foo() #调用foo()属性,会触发get方法 11 def __init__(self,x): 12 self.x = x 13 14 print(Bar.x) # 1、类属性比描述符有更高的优先级,会触发get方法 15 16 Bar.x=1 #2、 自己定义了一个类属性,并赋值给x,跟描述符没有关系,所以他不会触发描述符的方法 17 print(Bar.__dict__) 18 19 print(Bar.x) 20 21 ------------------------------------------------------------------ 22 23 ===>get方法 24 None 25 {'__module__': '__main__', 'x': 1, '__init__': <function Bar.__init__ at 0x0242E108>, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None} 26 1
数据描述符>实例属性
示例:
1 #有get,set就是数据描述符,数据描述符比实例属性有更高的优化级 2 3 class Foo: 4 def __get__(self,instance,owner): 5 print('===>get方法') 6 def __set__(self, instance, value): 7 print('===>set方法') 8 def __delete__(self, instance): 9 print('===>delete方法') 10 11 class Bar: 12 x = Foo() # 调用foo()属性,会触发get方法 13 def __init__(self,x,y): 14 self.x = x 15 self.y = y 16 17 18 b1=Bar(1,2) #在自己的属性字典里面找,找不到就去类里面找,会触发__get__方法 19 b1.x #调用一个属性的时候触发get方法 20 b1.x=1 #为一个属性赋值的时候触发set方法 21 del b1.x #采用del删除属性时触发delete方法 22 23 24 25 ===>set方法 26 ===>get方法 27 ===>set方法 28 ===>delete方法
示例:
1 class Foo: 2 def __get__(self,instance,owner): 3 print('===>get方法') 4 5 def __set__(self, instance, value): 6 pass 7 8 class Bar: 9 x = Foo() 10 11 b1=Bar() 12 b1.x=1 # 触发的是非数据描述符的set方法 13 print(b1.__dict__) 14 15 16 ------------------------------------------ 17 {} #数据描述符>实例属性
类属性>数据描述符>实例属性
1 #类属性>数据描述符>实例属性 2 3 class Foo: 4 def __get__(self,instance,owner): 5 print('===>get方法') 6 def __set__(self, instance, value): 7 print('===>set方法') 8 def __delete__(self, instance): 9 print('===>delete方法') 10 11 class Bar: 12 x = Foo() #调用foo()属性,会触发get方法 13 14 b1=Bar() #实例化 15 Bar.x=11111111111111111 #不会触发get方法 16 b1.x #会触发get方法 17 18 del Bar.x #已经给删除,所以调用不了!报错:AttributeError: 'Bar' object has no attribute 'x' 19 b1.x
非数据描述符
示例1:
1 #非数据描述符没有set方法 2 class Foo: 3 def __get__(self,instance,owner): 4 print('===>get方法') 5 6 def __delete__(self, instance): 7 print('===>delete方法') 8 9 class Bar: 10 x = Foo() 11 12 b1=Bar() 13 b1.x #自己类中没有,就会去Foo类中找,所以就会触发__get__方法
===>get方法
示例2:
1 #实例属性>非数据描述符 2 class Foo: 3 def __get__(self,instance,owner): 4 print('===>get方法') 5 6 class Bar: 7 x = Foo() 8 9 b1=Bar() 10 b1.x=1 11 print(b1.__dict__) #在自己的属性字典里面,{'x': 1} 12 13 14 --------------------------------------------------------- 15 {'x': 1}
非数据描述符>找不到
1 #非数据描述符>找不到 2 3 class Foo: 4 def __get__(self,instance,owner): 5 print('===>get方法') 6 7 class Bar: 8 x = Foo() 9 def __getattr__(self, item): 10 print('------------>') 11 12 b1=Bar() 13 b1.xxxxxxxxxxxxxxxxxxx #调用没有的xxxxxxx,就会触发__getattr__方法 14 15 16 -------------------------------------------------- 17 #非数据描述符>找不到 18 19 class Foo: 20 def __get__(self,instance,owner): 21 print('===>get方法') 22 23 class Bar: 24 x = Foo() 25 def __getattr__(self, item): 26 print('------------>') 27 28 b1=Bar() 29 b1.xxxxxxxxxxxxxxxxxxx #调用没有的xxxxxxx,就会触发__getattr__方法 30 31 ------------------------------------------------- 32 ------------> #解发__getattr__方法
四、描述符的应用
示例1:
1 class Typed: #有__get__,__set__,__delete__ 就是:数据描述符 2 def __get__(self, instance,owner): 3 print('get方法') 4 print('instance参数【%s】' %instance) 5 print('owner参数【%s】' %owner) 6 7 def __set__(self, instance, value): 8 print('set方法') 9 print('instance参数【%s】' %instance) 10 print('value参数【%s】' %value) 11 12 def __delete__(self, instance): 13 print('delete方法') 14 print('instance参数【%s】'% instance) 15 class People: 16 name=Typed() #设置代理(代理的就是name属性) 17 def __init__(self,name,age,salary): 18 self.name=name #触发的是代理 19 self.age=age 20 self.salary=salary 21 22 p1=People('alex',13,13.3) 23 #'alex' #触发set方法 24 p1.name #触发get方法,没有返回值 25 p1.name='age' #触发set方法 26 print(p1.__dict__) #{'salary': 13.3, 'age': 13} 27 28 29 --------------------------------------------------------------- 30 set方法 31 instance参数【<__main__.People object at 0x0025F870>】 32 value参数【alex】 33 get方法 34 instance参数【<__main__.People object at 0x0025F870>】 35 owner参数【<class '__main__.People'>】 36 set方法 37 instance参数【<__main__.People object at 0x0025F870>】 38 value参数【age】 39 {'age': 13, 'salary': 13.3}
示例2:给字典属性传值
1 #给字典里面传入值 2 class Typed: 3 def __init__(self,key): 4 self.key=key 5 def __get__(self, instance, owner): 6 print('get方法') 7 print(self.key) 8 return instance.__dict__[self.key] #触发get方法,会返回字典的值 9 def __set__(self, instance, value): 10 print('set方法') 11 print(self.key) 12 instance.__dict__[self.key]=value #存在p1的属性字典里面 13 def __delete__(self, instance): 14 print('delete方法') 15 instance.__dict__.pop(self.key) 16 class People: 17 name=Typed('name') #name属性被Typed给代理了,t1._set_() self._set__() 18 def __init__(self,name,age,salary): 19 self.name=name 20 self.age=age 21 self.salary=salary 22 p1=People('alex',13,13.3) 23 #打印实例字典 24 print(p1.__dict__) 25 # #创建属性,给name赋值,相当于修改了name字典的值 26 p1.name='egon' #触发的是set方法 27 print(p1.__dict__) 28 # #删除name属性 29 # print(p1.__dict__) 30 del p1.name #触发的是delete方法 31 print(p1.__dict__) 32 33 34 ------------------------------------------------------------------------- 35 36 set方法 37 name 38 {'name': 'alex', 'age': 13, 'salary': 13.3} 39 set方法 40 name 41 {'name': 'egon', 'age': 13, 'salary': 13.3} 42 delete方法 43 {'age': 13, 'salary': 13.3}
示例3:实现类型检测的两种方法:
方法一:用return的方式:
1 #判断他传入的值是不是字符串类型 2 class Typed: 3 def __init__(self,key): 4 self.key=key 5 6 def __get__(self, instance, owner): 7 print('get方法') 8 return instance.__dict__[self.key] #触发get方法,会返回字典的值 9 10 def __set__(self, instance, value): 11 print('set方法') 12 if not isinstance(value,str): 13 print('你传入的类型不是字符串,错误') 14 return None #return的作用就是终止这个属性字典,让他的值设置不进字典中。 15 instance.__dict__[self.key]=value #存在p1的属性字典里面 16 17 def __delete__(self, instance): 18 print('delete方法') 19 instance.__dict__.pop(self.key) 20 21 class People: 22 name=Typed('name') #name属性被Typed给代理了,t1._set_() self._set__() 23 def __init__(self,name,age,salary): 24 self.name=name 25 self.age=age 26 self.salary=salary 27 28 #正常的情况下__dict__里面有没有'name':'alex' 29 p1=People('alex',13,13.3) 30 print(p1.__dict__) #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13} 31 32 #不正常的情况下,修改'name':213 不等于字符串,改成了int类型 33 p1=People(213,13,13.3) 34 print(p1.__dict__) #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13} 35 36 37 ------------------------------------------------------------------ 38 set方法 39 {'name': 'alex', 'age': 13, 'salary': 13.3} 40 set方法 41 你传入的类型不是字符串,错误 42 {'age': 13, 'salary': 13.3}
方法二:用raise抛出异常的方式,判断他传入的值是不是字符串类型,(写死了,不灵活)
1 #用抛出异常的方式,判断他传入的值是不是字符串类型 2 class Typed: 3 def __init__(self,key): 4 self.key=key 5 6 def __get__(self, instance, owner): 7 print('get方法') 8 return instance.__dict__[self.key] #触发get方法,会返回字典的值 9 10 def __set__(self, instance, value): 11 print('set方法') 12 if not isinstance(value,str): #判断是否是字符串类型 13 #方法一:return的方式 14 print('你传入的类型不是字符串,错误') 15 # return #return的作用就是终止这个属性字典,让他的值设置不进字典中。 16 #方法二: 17 raise TypeError('你传入的类型不是字符串') 18 # ##用抛出异常的方式,判断他传入的值是不是字符串类型 19 instance.__dict__[self.key]=value #存在p1的属性字典里面 20 21 def __delete__(self, instance): 22 print('delete方法') 23 instance.__dict__.pop(self.key) 24 25 class People: 26 name=Typed('name') #name属性被Typed给代理了,t1._set_() self._set__() 27 def __init__(self,name,age,salary): 28 self.name=name 29 self.age=age 30 self.salary=salary 31 32 #正常的情况下__dict__里面有没有'name':'alex' 33 # p1=People('alex',13,13.3) 34 # print(p1.__dict__) #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13} 35 36 #不正常的情况下,修改'name':213 不等于字符串,改成了int类型 37 p1=People(213,13,13.3) 38 print(p1.__dict__) #触发set方法,得到的值是{'salary': 13.3, 'name': 'alex', 'age': 13} 39 40 41 42 --------------------------------------------------------------- 43 Traceback (most recent call last): 44 set方法 45 你传入的类型不是字符串,错误 46 File "E:/三期笔记/Python_s3/学习/day28/test.py", line 37, in <module> 47 p1=People(213,13,13.3) 48 File "E:/三期笔记/Python_s3/学习/day28/test.py", line 28, in __init__ 49 self.name=name 50 File "E:/三期笔记/Python_s3/学习/day28/test.py", line 17, in __set__ 51 raise TypeError('你传入的类型不是字符串') 52 TypeError: 你传入的类型不是字符串
类型检测加强版
示例:4:用raise抛出异常的方式,判断传入的值是什么类型,同时可以判断多个属性 (推荐写法)
1 #用抛出异常的方式,判断他传入的值是什么类型 (不写死的方式,判断传入值的类型) 2 class Typed: 3 def __init__(self,key,expected_type): 4 self.key=key 5 self.expected_type=expected_type 6 7 def __get__(self, instance, owner): 8 print('get方法') 9 return instance.__dict__[self.key] #触发get方法,会返回字典的值 10 11 def __set__(self, instance, value): 12 print('set方法') 13 if not isinstance(value,self.expected_type): 14 raise TypeError('%s 你传入的类型不是%s' %(self.key,self.expected_type)) #用抛出异常的方式,判断他传入的值是什么类型,同时可以判断多个属性的类型 15 instance.__dict__[self.key]=value #存在p1的属性字典里面 16 17 def __delete__(self, instance): 18 print('delete方法') 19 instance.__dict__.pop(self.key) 20 21 class People: 22 name=Typed('name',str) #name设置代理Typed 23 age=Typed('age',int) #age设置代理Typed 24 def __init__(self,name,age,salary): 25 self.name=name #alex传给代理,会触发set方法 26 self.age=age #age传给代理,会触发set方法 27 self.salary=salary 28 29 #name是字符串,age是整型,salary必须是浮点数 30 #正确的方式 31 p1=People('alex',13,13.3) 32 33 #传入错误的类型,会判断传入值的类型 34 #name要求传入的srt,但这里传入的是整型,所以会报错,说你传入的不是srt类型 35 p1=People(213,13,13.3)
十八、__enter__ 和 __ exit__
1、操作文件推荐写法:
1 with open('a.txt') as f: 2 '代码块'
2、上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__ 和__exit__方法
1 class Open: 2 def __init__(self,name): 3 self.name=name 4 5 def __enter__(self): 6 print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') 7 return self # f 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 print('with中代码块执行完毕时执行我啊') 10 11 12 with Open('a.txt') as f: #with语句,触发__enter__,返回值赋值给f 13 print('=====>执行代码块') 14 print(f,f.name) 15 16 17 -------------------------------------------------- 18 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 19 =====>执行代码块 20 <__main__.Open object at 0x002AC750> a.txt 21 with中代码块执行完毕时执行我啊
3、执行代码块
__exit__()中的三个参数分别代表异常类型,异常值,追溯信息,with语句中的代码块出现异常,则with后的代码都是无法执行的
没有异常的情况下,整个代码块运行完毕后去触发__exit__(),它的三个参数都会运行
1 class Foo: 2 def __init__(self, name): 3 self.name = name 4 5 def __enter__(self): 6 print('执行enter') 7 return self # 2、拿到的结果是self,并赋值给f 8 9 def __exit__(self, exc_type, exc_val, exc_tb): # 4、触发__exit__,然后执行print() 10 print('执行exit') 11 print('-----',exc_type) 12 print('-----',exc_val) 13 print('-----',exc_tb) 14 15 16 with Foo('a.txt') as f: # 1、with触发的是__enter__,拿到的结果是self并赋值给f; 17 print(f) # 3、然后会执行with代码块,执行完毕后 18 print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad) 19 print(f.name) 20 print('000000000000000000000000000000000000000000000000000000') 21 22 --------------------------------------------------------------------------------- 23 24 25 Traceback (most recent call last): 26 File "E:/三期笔记/Python_s3/学习/day28/test.py", line 18, in <module> 27 print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad) 28 NameError: name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined 29 执行enter 30 <__main__.Foo object at 0x004F07F0> 31 执行exit 32 ----- <class 'NameError'> 33 ----- name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined 34 ----- <traceback object at 0x008300A8>
4、有返回值
如果__exit__() 返回值为True,那么异常会被清空,就好像啥都没有发生一样,with后的语句正常执行
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 5 def __enter__(self): 6 print('执行enter') 7 return self 8 # '''class、异常值、追踪信息''' 9 def __exit__(self, exc_type, exc_val, exc_tb): #2、有异常的时候,就会触发__exit__方法 10 print('执行exit') 11 print('====',exc_type) 12 print('====',exc_val) 13 print('====',exc_tb) 14 return True #3、没有return True就会报错,如果有return True异常自己吃了,不报异常 15 16 with Foo('a.txt') as f: 17 print(f) 18 print(assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad) #1、有异常的情况,他就会触发__exit__ 19 print(f.name) #不执行这行,直接打印下面那行 20 print('000000000000000000000000000000000000000000000000000000') #4、最后打印这行 21 22 ----------------------------------------------------------------------- 23 24 执行enter 25 <__main__.Foo object at 0x003907F0> 26 执行exit 27 ==== <class 'NameError'> 28 ==== name 'assfsfdsfdsfdsfffsadfdsafdsafdsafdsafdsafdsafdsafdsad' is not defined 29 ==== <traceback object at 0x023E00A8> 30 000000000000000000000000000000000000000000000000000000
总结:
with obj as f:
'代码块'
1、with obj ——》 触发obj.__enter__(),拿到返回值
2、as f ————》 f = 返回值
3、with obj as f 等同于 f = obj.__enter__()
4、执行代码块
一、没有异常的情况下,整个代码块运行完毕后触发__exit__,它的三个参数都为None
二、有异常的情况下,从异常出现的位置直接触发__exit__
a:如果__exit__的返回值为True,代表吞掉了异常
b:如果__exit__的返回值不为True,代表吐出了异常
c:__exit__的运行完毕就代表整个with语句的执行完毕
用途:
1、使用withhi把语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动干预。
2、在需要管理一些资源,比如:文件,网络连接(TCP协议建连接、传输数据、关连接)和锁(进程,线程)的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关心这个问题。
十九、类的装饰器
示例1:类的装饰器的基本原理