python上下文管理器
Posted Sakura
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python上下文管理器相关的知识,希望对你有一定的参考价值。
上下文管理器和with模块
上下文管理器对象存在的目的是管理with语句。with语句的目的是简化try/finally模式。这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码异常,return语句调用或sys.exit()调用而中止,也会执行操作。finally子句中的代码通常用于释放重要的资源,或者还原临时变更的状态。
上下文管理器包含__enter__和__exit__两个方法。with语句开始运行时,会在上下文管理器对象上调用__enter__方法。with语句运行结束后,会在上下文管理器对象上调用__exit__方法,以此扮演finally子句的角色。
创建一个mirror.py文件,演示把文件对象当成上下文管理器使用:
with open(‘mirror.py‘) as fp: #fp绑定到打开的文件上,因为文件的方法返回self src = fp.read(60) #读取数据 len(src) print(fp) print(fp.closed) print(fp.encoding) print(fp.read(60)) #结果 <_io.TextIOWrapper name=‘mirror.py‘ mode=‘r‘ encoding=‘cp936‘> #fp对象仍然可用 True cp936 #读取fp对象的属性 ValueError: I/O operation on closed file. #with块的末尾,调用TextIOWrapper.__exit__方法把文件关闭了
第一行代码中,执行with后面的表达式得到的结果是上下文管理器对象,不过,把值绑定到目标变量上(as子句)是在上下文管理器对象上调用__enter__方法的结果。
示例中open()函数返回TextIOWrapper类实例,而该实例的__enter__方法返回self,__enter__方法除了会返回上下文管理器对象之外,还可能返回其他对象。
不管控制流程以哪种方式退出with块,都会在上下文管理器对象上调用__exit__方法,而不是在__enter__方法返回的对象上调用。
with语句的as子句是可选的。对于open()函数来说,必须加上as子句以便获取文件引用。而有些上下文管理器会返回None。
一个上下文管理器类:
class LookingGlass: def __enter__(self): #除了self不传入其他参数 import sys self.original_write = sys.stdout.write #把原来的sys.stdout.write保存在一个实例属性中 sys.stdout.write = self.reverse_write #替换成自己编写的方法 return ‘JABBERWOCKY‘ #返回字符串 def reverse_write(self, text): #内容反转打印 self.original_write(text[::-1]) def __exit__(self, exc_type, exc_val, exc_tb): import sys sys.stdout.write = self.original_write #将方法改回 if exc_type is ZeroDivisionError: #异常检测 print(‘please DO NOT divide by zero!‘) return True
解释器调用__enter__方法时,除了隐式的self之外,不会传入任何参数。传递给__exit__的有三个参数:
exc_type //异常类
exc_value //异常实例。有时会有参数传递给异常构造方法,这些参数可以使用exc_value.args获取
traceback //traceback对象
测试:
from mirror import LookingGlass with LookingGlass() as what: #上下文管理器上调用__enter__方法,把返回结果绑定到what上 print(‘hello world!‘) print(what) print(what) print(‘hello world!‘)
结果:
!dlrow olleh YKCOWREBBAJ #打印出的内容是反向的 JABBERWOCKY #with语句执行完毕,输出不再反向 hello world!
也可用在with块之外使用LookingGlass类:
from mirror import LookingGlass manager = LookingGlass() #实例化 print(manager) #审查对象 moster = manager.__enter__() #调用__enter__ print(moster == ‘JABBERWOCKY‘) #见结果,输出反向 print(moster) #输出反向 print(manager) #输出反向 manager.__exit__(None, None, None) #调用__exit__还原函数 print(moster == ‘JABBERWOCKY‘) print(moster) #结果 <mirror.LookingGlass object at 0x0000016F4A5647B8> eurT YKCOWREBBAJ >8B7465A4F6100000x0 ta tcejbo ssalGgnikooL.rorrim< True #输出结果正常 JABBERWOCKY
contextlib模块中的实用工具
closing //如果对象提供了close方法,但没有实现__enter__/__exit__协议,那么可用使用这个函数构建上下文管理器
suppress //构建临时忽略指定异常的上下文管理器
@contextmanager //这个装饰器把简单的生成器函数变为上下文管理器
ContextDecorator //这是个基类,用于定义基于类的上下文管理器。这种上下文管理器也能用于装饰函数,在受管理的上下文中运行整个函数。
ExitStack //这个上下文管理器能进入多个上下文管理器。with块结束时,ExitStack按照后进先出的顺序调用栈中各个上下文管理器的__exit__方法。
@contextmanager
示例,类似Lookingglass:
@contextlib.contextmanager #应用装饰器 def looking_glass(): import sys original_write = sys.stdout.write def reverse_write(text): original_write(text[::-1]) sys.stdout.write = reverse_write() yield ‘JABBERWOCKY‘ #产出一个值,这个值会绑定在with语句中as子句的目标变量上。执行with语句块中代码时,这个函数会在这个点暂停 sys.stdout.write = original_write #控制权跳出with块,继续执行yield语句之后的代码
@contextlib.contextmanager装饰器会把函数包装成实现__enter__和__exit__方法的类。
1.这个类__enter__方法有如下作用
(1)调用生成器函数,保存生成器对象
(2)调用next(),执行到yield关键字所在位置
(3)返回next()产出的值,以便把产出的值绑定的with/as语句中的目标变量上
2.with块终止时,__exit__方法会做以下几件事情
(1)检查有没有把异常传递给exc_type;如果有,调用gen.throw(exception),在生成器函数定义体中包含yield关键字的那一行抛出异常
(2)否则调用next()函数,继续执行生成器函数定义体中的yield语句之后的代码。
上面looking_glass()函数有一个错误,2(1)步骤如果抛出错误,却没有错误处理代码,将无法执行sys.stdout.write = original_write。
添加异常处理代码:
@contextlib.contextmanager #应用装饰器 def looking_glass(): import sys original_write = sys.stdout.write def reverse_write(text): original_write(text[::-1]) sys.stdout.write = reverse_write() msg = ‘‘ try: yield ‘JABBERWOCKY‘ except ZeroDivisionError: msg = ‘Please DO NOT divide by zero!‘ finally: sys.stdout.write = original_write if msg: print(msg)
使用@contextmanager装饰器时,要把yield语句放在try/finally语句中(或者with语句中),因为永远不知道用户会在上下文管理器做什么。
以上来自《流畅的python》
以上是关于python上下文管理器的主要内容,如果未能解决你的问题,请参考以下文章