详解python with 上下文管理器
作为一个Java为母语的程序员来讲,学习起其他新的语言就难免任何事都与Java进行横向对比。Java7引入了能省去许多重复代码的try-with-resources特性,不用每回try/finally来释放资源(不便之处有局部变量必须声明在try之前,finally里还要嵌套try/catch来处理异常)。比如下面的Java代码
try(InputStreaminputStream=newFileInputStream("abc.txt")){ System.out.println(inputStream.read()); }catch(Exceptionex){ }
它相应的不使用try-with-resources语法的代码就是
InputStreaminputStream=null; try{ inputStream=newFileInputStream("abc.txt"); }catch(Exceptionex){ }finally{ if(inputStream!=null){ try{ inputStream.close(); }catch(Exceptionex){ } } }
类似的Python也有自己的try-with-resources写法,就是with关键字,它的概念叫做上下文管理器(ContextManager)。
with关键字的使用
withopen('some_file','w')asopened_file: opened_file.write('Hola!')
以上的代码相当于
opened_file=open('some_file','w') try: opened_file.write('Hola!') finally: opened_file.close()
也就是with关键字打开的资源会在with语句块结束后自动调用相应的方法自动释放(无论with中操作是否有异常)。
with用起来是很方便的,但是什么样的资源可以用with关键字?Python是怎么知道要调用哪个方法来关闭资源的?进而如何实现自己的支持上下文管理器的Python类。
再次回顾Java的try-with-resources语法,try(...)括号支持的类必须是实现了AutoCloseable接口,它的接口方法是
publicvoidclose()throwsIOException
也就是Java的try-with-resources语法会自动调用以上方法来释放资源,要实现可被自动释放的Java就只须遵照这一规则就行。
而在Python中,能被with的类有两种实现方式
实现基本方法以支持上下文管理器的类
一个Python类要能被用于with上下文,必须实现至少__enter__和__exit__方法。这两个方法的意思好理解,一个是创建资源后,后者是退出with语句块后。请看下面的例子
classFile(object): def__init__(self,file_name,method): self.file_obj=open(file_name,method) def__enter__(self): print("---enter") returnself.file_obj def__exit__(self,type,value,traceback): print("---exit") self.file_obj.close() withFile('data.txt','r')asdata_file: print(data_file.read())
假设data.txt文件中的内容是
hello
world
那么以上程序执行后的输出就是
--enter
hello
world
---exit
- __enter__返回的值作为with...asdata_file中的data_file变量的值,如果__enter__没有返回,data_file得到的就是NoneTypeobject了。
- __exit__可利用来释放资源
- 没有__enter__方法试图用with的写法执行时会得到AttributeErro:__enter__异常
- 同样,没有__exit__方法试图用with的写法执行时会得到AttributeErro:__exit__异常
- __exit__有其他额外的三个参数,可获得资源的值,以及能处理with块中执行出现异常的情况
- __exit__的返回值也有用途,如果它返回True则出现的异常不再向外传播,其他值的话直接向外抛
利用生成器(Generator)和装饰器创建支持上下文管理器的方法
此种方式比较简单,不过逻辑控制上没有这么强。
fromcontextlibimportcontextmanager @contextmanager defopen_file(name,method): f=open(name,method) yieldf f.close()
使用f的执行代码将被放置在yieldf所处的位置,with使用以上方法。yield后的f变量将是with...as后的变量值
withopen_file('some_file','w')asfile_object: file_object.write('hola!')
这里也要注意异常处理的情况,比如把上面代码打开文件的模式换作r,仍然试图去写文件,这样在open_file方法的yieldf位置将产生异常,会造成f.close()得不到执行,不能正确释放该资源。
欲更具防御性,前面的yieldf可以扩展也如下的形式
try: yieldf exceptExceptionasex: pass#处理异常,或继续向外抛 finally: f.close()
@contextmanager装饰器内部也是封装为一个实现了__enter__和__exit__方法的对象。
参考链接:ContextManagers
以上就是详解pythonwith上下文管理器的详细内容,更多关于pythonwith上下文管理器的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。