编程中,在我们使用系统资源的时候,如需打开一个文件用于读写,加锁确保线程安全,在使用完成后需要关闭该文件,释放我们所占用的资源。通常,我们可以将其封装在一个try...except...finally
语句块中,这样能够确保在运行产生错误的情况我们也能释放相关资源。但每次都要记得手动关闭,着实麻烦。毕竟懒是是第一生产力,有没有更简便的写法?
答案是:有。Python提供了一个with
表达式,只要将相关的代码块放入with
表达式中,它便能起到一个类似try...except
的作用,这使得我们不需要每次使用完成后再去手动关闭相关资源了。其使用语法为:
with_stmt ::= "with" with_item ("," with_item)* ":" suite
with_item ::= expression ["as" target]
例如:
with open('myfile.txt', 'w') as fd:
# read or write here
BLOCK1
...
# other work
# outside with statement
# other work
BLOCK2
...
实际上,with
表达式是将用户相关代码块封装在一个上下文管理器(context manager)中。所谓上下文管理器,通俗的讲,其实就是一个包含特定方法对__enter__, __exit__
的对象,该方法对使得用户可以在进入相关代码块前设置好所需上下文环境,并在相关代码块退出后做一些善后工作,如释放资源,解锁等。
with的执行流程如下所示:
- 获取上下文管理器。例如上面代码块中的
open('myfile.txt', 'w')
会将文件自身返回; - 上下文管理器的
__enter__
方法被调用; - 第二步中的返回值被赋值到
target
,如果target
存在的话; - 执行
with
中的代码块,BLOCK1 - 上下文管理器的
__exit__
方法被调用。 - 判断代码块的退出原因,如果是因为一场退出,执行第7步,如果因为除了异常以外的原因(如正常退出),忽略第七步;
- 判断第5步中
__exit__
的返回值,如果是True
,忽略异常继续执行后面代码,如果是False
则抛出该异常,终止执行。
上面几个步骤可以用伪代码表示如下:
context_manager = SomeKindOfManager()
target = context_manager.__enter__()
try:
BLOCK1
finally:
result = context_manager.__exit__()
if reason is exception:
if result:
suppress exception
else
raise exception
BLOCK2
上面提到的__exit__
方法接收三个参数,分别是exception_type, exception_value, exception_traceback
。
下面,就用个小栗子作为结束吧。
class MyContextManager(object):
def __enter__(self):
return 'hello world'
def __exit__(self, exc_type, exc_val, exc_traceback):
print('Byebye')
with MyContextNanager() as msg:
print(msg)
其结果为:
hello world
Byebye
最后说一句,Python定义了许多上下文管理器用于支持线程安全、关闭文件或其他资源等。详情请参阅contextlib。
本文首发于个人公众号TensorBoy。如果你觉得内容还不错,欢迎分享并关注我的公众号TensorBoy,扫描下方二维码获取更多精彩原创内容!