在示例代码中经常会看到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语句更详细的介绍见如下参考链接: