话说with语句

在示例代码中经常会看到with引导的代码片断,本篇解释with语句的用法和原理。

背景

涉及资源操作时,为了避免出现资源得不到正常释放,需要将代码放在异常捕捉块内。对这种常见情境,Python提供了一种方便的语法:with语句。

with 语句是从 Python 2.5 开始引入的一种与异常处理相关的功能(2.5 版本中要通过 from future import with_statement 导入后才可以使用),从 2.6 版本开始缺省可用(参考 What’s new in Python 2.6? 中 with 语句相关部分介绍)。with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的”清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。

语法

with context_expression [as target(s)]:
        with-body
  • context_expression译为上下文表达式,它构成了一个上下文管理对象。
  • 它并不会因as而赋值给target(s),而是将该对象的_enter()方法返回值赋值给target(s)
  • taget(s)也可以是一个元组,该元组需要用()包围起来。

示例

# 文件操作

# 1) 没有使用 with 语句
file = open('file_path', 'w') 
file.write('hello world !') 
file.close() 

# 2) 没有使用 with 语句,利用 try 语句
file = open('file_path', 'w') 
try: 
	file.write('hello world') 
finally: 
	file.close() 

1)代码中没有应对异常出现的能力。若文件在写入过程中出现问题,则文件无法正常关闭,即不能执行file.close()语句。
2)代码增加了try语句,增强了代码的处理异常能力,但代码也显得冗长。想象一个当我们大量操作文件资源时,总需要这样的try语句块。

用with语句可以使代码更加简洁,且实现了资源的安全管理。

# using with statement 
with open('file_path', 'w') as file: 
	file.write('hello world !') 

原理

某个类能够成为上下文管理器,是由于该类实现了两个内建方法:_enter_exit

  • context manager. _enter () :进入上下文管理器的运行时上下文,在语句体执行前调用。with 语句将该方法的返回值赋值给 as 子句中的 target,如果指定了 as 子句的话
  • context manager. _exit (exc_type, exc_value, exc_traceback) :退出与上下文管理器相关的运行时上下文,返回一个布尔值表示是否对发生的异常进行处理。参数表示引起退出操作的异常,如果退出时没有发生异常,则3个参数都为None。如果发生异常,返回 True 表示不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理。如果该方法内部产生异常,则会取代由 statement-body 中语句产生的异常。要处理异常时,不要显示重新抛出异常,即不能重新抛出通过参数传递进来的异常,只需要将返回值设置为 False 就可以了。之后,上下文管理代码会检测是否 exit () 失败来处理异常。

参考

关于with语句更详细的介绍见如下参考链接: