contextlib

作者 新城 日期 2017-09-01
contextlib

在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源
的一个方法是使用try…finally:

1
2
3
4
5
6
try:
f = open('/path/to/file', 'r')
f.read()
finally:
if f:
f.close()

写try…finally非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,
所以上面的代码可以简化为:

1
2
with open('/path/to/file', 'r') as f:
f.read()

并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。

实现上下文管理是通过enterexit这两个方法实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Query(object):

def __init__(self, name):
self.name = name

def __enter__(self):
print('Begin')
return self

def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print('Error')
else:
print('End')

def query(self):
print('Query info about %s...' % self.name)

#自己写的资源对象用于with语句
with Query('Bob') as q:
q.query()

所谓上下文关系是指: 先后执行顺序

@contextmanager

编写enterexit仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,
上面的代码可以改写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from contextlib import contextmanager

class Query(object):

def __init__(self, name):
self.name = name

def query(self):
print('Query info about %s...' % self.name)

@contextmanager
def create_query(name):
print('Begin')
q = Query(name)
yield q
print('End')

#执行
with create_query('Bob') as q:
q.query()

#输出
Begin
Query info about Bob...
End

理解相当于装饰器 将函数进行包装、

很时候我们希望在执行某段代码的前后输出特定的字符 可以使用

1
2
3
4
5
6
7
8
9
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)

with tag("h1"):
print("hello")
print("world")

输出


hello
world