赞
踩
在Python中,读写文件要注意使用完毕后必须进行关闭(文件对象占用大量资源并且同一时间操作系统只能打开有限数量的文件。之前已经介绍了利用 try...finally
机制关闭文件资源的方法:
try:
f = open('/path/to/file', 'r')
f.read()
finally:
if f:
f.close()
但是,写 try...finally
非常繁琐,所以后续又介绍了使用 with
语句的方法。with
语句允许我们非常方便地使用资源,而不必担心资源没有关闭。使用 with
语句改写后,上面的代码就可以简化为:
with open('/path/to/file', 'r') as f:
f.read()
事实上,并不是只有 open()
函数返回的文件对象才能使用 with
语句。任何对象,只要正确实现了上下文管理,就可以用于 with
语句。
上下文管理是通过 __enter__
和 __exit__
这两个方法实现的。下面的类就实现了这两个方法:
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()
编写 __enter__
和 __exit__
还是太繁琐了,有没有更简单的办法呢?有!Python的标准库 contextlib
提供了更简单的写法,借助它,上面的代码可以改写为:
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')
简单解析一下,我们定义一个简单的 Query
类,只有一个 query()
方法。同时我们定义了一个 create_query()
函数,由于这个函数包含 yield
关键字,所以实际上它是一个生成器。不过这个生成器只生成和抛出一个 Query
类的对象。
@contextmanager
这个装饰器接收一个生成器,并为生成器抛出的对象添加上下文管理的功能。这样 with
语句就可以正常地工作了:
with create_query('Bob') as q:
q.query()
很多时候,我们希望在某段代码执行前后自动执行特定代码,也可以用 @contextmanager
实现。例如:
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
上述代码执行结果为:
<h1>
hello
world
</h1>
代码的执行顺序是:
with
语句首先执行 yield
前面的语句,因此打印出 <h1>
;yield
之后会跳出生成器(tag()
函数),执行 with
语句内部的所有语句,因此打印出 hello
和 world
;with
语句内部的所有语句继续回到生成器;yield
后面的语句,打印出 </h1>
;借助 @contextmanager
装饰器,我们能够更加方便地实现上下文管理。
前面一节介绍了如何为一个对象实现上下文管理功能,使得它能被作用于 with
语句。但是,得自己编写一个生成器还是很麻烦!有没有更更方便的办法呢?有!我们可以用 closing()
方法!
closing()
的本质如下:
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
其实它就是一个经过 @contextmanager
装饰的生成器,它的作用就是把任意对象变为上下文对象,使其支持 with
语句。
再改写一次上面 Query
的例子:
from contextlib import closing
class Query(object):
def __init__(self, name):
self.name = name
def query(self):
print('Query info about %s...' % self.name)
with closing(Query('Bob')) as q:
q.query()
这次更加简单了~
@contextlib
还有一些其他装饰器,可以帮助我们编写更简洁的代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。