一、上下文管理协议
1.1、什么叫上下文管理协议?
with open('a.txt') as f: '代码块'
即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
with语句小结:
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.2、方法使用
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') with Open('a.txt') as f: #触发__enter__方法 print('=====>执行代码块') print(f,f.name) #<__main__.Open object at 0x0000022813E55C88> a.txt
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('异常产生') print('0'*100) #------------------------------->不会执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) #<class 'AttributeError'> print(exc_val) #产生异常 print(exc_tb) #<traceback object at 0x000001E54419AF08> return True with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('产生异常') print('0'*100) #------------------------------->会执行
模拟open:
class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def __enter__(self): print('enter') self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) #拿到文件句柄 return self.f def __exit__(self, exc_type, exc_val, exc_tb): # print('exit') self.f.close() return True def __getattr__(self, item): return getattr(self.f,item) with Open('a.txt','w') as f: #f==open(self.filepath,mode=self.mode,encoding=self.encoding) print(f) #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'> f.write('aaaaaa') f.wasdf #抛出异常,交给__exit__处理
1.3、上下文管理用途
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
二、描述符
2.1、什么是描述符?
描述符本质就是一个新式类,在这个新式类中,至少实现__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass
2.2、描述符是干什么的?
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age p1=People('alex',18) #触发str类的__set__,触发int类的__set__ print("========") #描述符Str的使用 print(p1.name) #Str调用 None p1.name='egon' del p1.name #描述符Int的使用 p1.age #Int调用 p1.age=18 #Int设置... del p1.age #Int删除... print(p1.__dict__) #{} print(People.__dict__) #{'__init__': <function People.__init__ at 0x000001714E4CE620>, 'age': <__main__.Int object at 0x000001714E4D46D8>, '__module__': '__main__', 'name': <__main__.Str object at 0x000001714E4D46A0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'People' objects>} #补充 print(type(p1) == People) #True,type(obj)其实是查看obj是由哪个类实例化来的 print(type(p1).__dict__ == People.__dict__) #True