python之context manager学习

What is context manager?

A basic issue in programming is resource management: a resource is anything in limited supply, notably file handles, network sockets, locks, etc., and a key problem is making sure these are released after they are acquired. If they are not released, you have a resource leak, and the system may slow down or crash. More generally, you may want cleanup actions to always be done, other than simply releasing resources.

就像其名字一样,context manager负责管理一定的上下文环境,按照你的设定,及时分配一定的资源,在完成相应的工作之后,及时释放这些资源. 而且不止是释放资源,你还可以做其他你想的事情……

Python提供了一种特定的语法结构可以用来做如此工作,许多类型都支持context text,比如buit-in File对象.

常用的操作

1
2
3
4
5
with open(file_name, 'rb') as f:
lines = f.readlines()
# other opoerations

print f.closed # True

i.e.在这个例子里context manager会自动关闭f文件对象,不需要用户显示地执行代码关闭,更加安全.

仔细研究会发现,这类对象,需要实现__enter____exit__两个方法属性,上面的例子等价于

1
2
3
4
5
6
7
f = open(file_name, 'rb')
f.__enter__()
try:
BLOCK Operations
finally:
f.__exit__()

How to implement?

首先,通过实现__enter____exit__这两个协议方法,我们可以使用with expre [as var]:语法来使用context manager.如:

1
2
3
4
5
6
7
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
self.file_obj.close()

__enter__将返回值传入as后面的var变量,block退出时执行__exit__方法,更通常一些,__exit__方法应该返回一个bool值表示是否发生异常. 基于以上File类的定义可以使用下面的withstatement.

1
2
with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')

处理异常也基本同理.

contextlib: Utilities for with-statement contexts

看一下这个built-in moudulecontextlib

This module provides utilities for common tasks involving the with statement. For more information see also Context Manager Types and With Statement Context Managers.

contextlib.contextmanager

官方的例子是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from contextlib import contextmanager

@contextmanager
def tag(name):
print "<%s>" % name
yield
print "</%s>" % name

>>> with tag("h1"):
... print "foo"
...
<h1>
foo
</h1>

这个函数是个decorator函数,参考之前的一篇,它可以将函数包装为context manager,无需自己显示为其实现__enter____exit__方法.

这个函数返回一个iterator,可以使用yield关键字

At the point where the generator yields, the block nested in the with statement is executed.

yield之后,便会执行块下的内容.

改写上面的例子为

1
2
3
4
5
6
7
@contextmanager
def opened(filename, mode="r"):
f = open(filename, mode)
try:
yield f
finally:
f.close()