Python 版的 try-with-resources -- with 上下文管理器

栏目: Python · 发布时间: 7年前

内容简介:作为一个  Java 为母语的程序员来讲,学习起其他新的语言就难免任何事都与 Java 进行横向对比。Java 7 引入了能省去许多重复代码的它相应的不使用 try-with-resources 语法的代码就是类似的  Python 也有自己的  try-with-resources 写法,就是 with 关键字,它的概念叫做上下文管理器(Context Manager)。

作为一个  Java 为母语的 程序员 来讲,学习起其他新的语言就难免任何事都与 Java 进行横向对比。Java 7 引入了能省去许多重复代码的 try-with-resources 特性,不用每回 try/finally 来释放资源(不便之处有局部变量必须声明在  try 之前,finally 里还要嵌套 try/catch 来处理异常)。比如下面的  Java 代码

try(InputStream inputStream = new FileInputStream("abc.txt")) {      System.out.println(inputStream.read());  } catch (Exception ex) {  }

它相应的不使用 try-with-resources 语法的代码就是

InputStream inputStream = null;
try {
    inputStream = new FileInputStream("abc.txt");
} catch (Exception ex) {
} finally {
    if(inputStream != null) {
        try {
            inputStream.close();
        } catch (Exception ex) {
        }
    }
}

类似的  Python 也有自己的  try-with-resources 写法,就是 with 关键字,它的概念叫做上下文管理器(Context Manager)。

with 关键字的使用

with open('some_file', 'w') as opened_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 接口,它的接口方法是

public void close() throws IOException

也就是 Java 的 try-with-resources 语法会自动调用以上方法来释放资源,要实现可被自动释放的 Java 就只须遵照这一规则就行。

而在 Python 中,能被 with 的类有两种实现方式

实现基本方法以支持上下文管理器的类

一个 Python 类要能被用于 with 上下文,必须实现至少 __enter____exit__ 方法。这两个方法的意思好理解,一个是创建资源后,后者是退出 with 语句块后。请看下面的例子

class File(object):
    def __init__(self, file_name, method):
        self.file_obj = open(file_name, method)
 
    def __enter__(self):
        print("---enter")
        return self.file_obj
 
    def __exit__(self, type, value, traceback):
        print("---exit")
        self.file_obj.close()
 
 
with File('data.txt', 'r') as data_file:
    print(data_file.read())

假设 data.txt 文件中的内容是

hello  world

那么以上程序执行后的输出就是

--enter  hello  world  ---exit
  1. __enter__  返回的值作为  with ... as data_file  中的  data_file  变量的值,如果  __enter__  没有返回, data_file  得到的就是 NoneType  object 了。
  2. __exit__  可利用来释放资源
  3. 没有  __enter__  方法试图用  with  的写法执行时会得到  AttributeErro: __enter__   异常
  4. 同样,没有  __exit__  方法试图用  with  的写法执行时会得到  AttributeErro: __exit__   异常
  5. __exit__  有其他额外的三个参数,可获得资源的值,以及能处理 with  块中执行出现异常的情况
  6. __exit__  的返回值也有用途,如果它返回 True  则出现的异常不再向外传播,其他值的话直接向外抛

利用生成器(Generator) 创建支持上下文管理器的方法

此种方式比较简单,不过逻辑控制上没有这么强。

from contextlib import contextmanager
 
@contextmanager
def open_file(name, method):
    f = open(name, method)
    yield f
    f.close()

使用 f 的执行代码将被放置在  yield f 所处的位置, with 使用以上方法。 yield 后的 f 变量将是  with...as 后的变量值

with open_file('some_file', 'w') as file_object:
    file_object.write('hola!')

这里也要注意异常处理的情况,比如把上面代码打开文件的模式换作 r , 仍然试图去写文件,这样在  open_file 方法的  yield f 位置将产生异常,会造成 f.close() 得不到执行,不能正确释放该资源。

欲更具防御性,前面的 yield f 可以扩展也如下的形式

try:
    yield f
except Exception as ex:
    pass  #处理异常,或继续向外抛
finally:
    f.close()

参考链接: Context Managers


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Bulletproof Web Design

Bulletproof Web Design

Dan Cederholm / New Riders Press / 28 July, 2005 / $39.99

No matter how visually appealing or packed with content a Web site is, it isn't succeeding if it's not reaching the widest possible audience. Designers who get this guide can be assured their Web site......一起来看看 《Bulletproof Web Design》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具