What
PEP(python2.6)中有个新的语法糖with
用来简便的用于try/finally
语法。类型contextmanager
有两个方法__enter__
和 __exit__
, 这两个方法如其名,被调用在进入with和离开with作用域的时候。
注意,无论是否异常__exit__
都会被调用.
最常见的代码:
with open(filename) as f:
for i in f:
print i
直观的感受就是不需要手动调用close关闭,会在离开with作用域的时候自动调用。有点类似C++对象管理资源的概念(后面就有个类似auto_ptr的类库)。
with还可以嵌套的
with A() as a, B() as b:
suite
等价于:
with A() as a:
with B() as b:
suite
来个例子,读取A文件内容另存到B文件,是不是很方便?
with open(A) as a, open(B, 'w') as b:
b.write(a.read())
How
定义
-
contextmanager.__enter__()
进入运行时的with作用域是调用,返回as
使用的对象 -
contextmanager.__exit__(*exc_type*, *exc_val*, *exc_tb*)
离开运行时的with作用域是调用,返回一个boolean 标志,表明发生的任何异常是否被压制。如果有异常发生在with作用域,exc_type, exc_val, exc_tb包含异常信息,如果没有异常发生,三个参数均为None。如果返回True
压制with作用域发生的异常,继续执行with,返回False
异常会在这个函数结束后继续传播。
来个例子, 实现个普遍适用的管理器,省去每次使用都需要定义一个类
class GeneratorContextManager(object):
def __init__(self, gen):
# 保存generator对象
self.gen = gen
def __enter__(self):
# 返回`as`引用的对象
return self.gen.next()
def __exit__(self, type, value, traceback):
try:
# return False, 并且完成调用
self.gen.next()
except StopIteration:
# 捕获StopIteration,迭代结束的异常,无需抛出
pass
def contextmanager(func):
# 定义装饰器
def _f():
return GeneratorContextManager(func())
return _f
@contextmanager
def fun():
# 被装饰的函数必须使用yield,或者说必须是一个迭代器,并且只有一次迭代
f = open('run.py')
# yield 对象为as使用的对象
yield f
f.close()
with fun() as f:
# 使用方法
print f
模拟contextlib
中的方法,实现普遍适用的环境管理器,迭代器是为了实现goto
方法。(看过tornado协程的同学一定会觉得亲切,后面有空再写一遍专门介绍迭代器)
Why
前面说到with是个语法糖,简化try/finally
的操作。通俗点说就是为了写代码的时候少写点下面这样的代码
try:
f = open(filenmae)
finally:
f.close()
而且防止很多粗心鬼忘记写上面的代码因为有点繁琐,写成
with open(filename) as f:
dosmth
既简单又简洁,提供程序健壮,防止资源泄漏
工具类
前面提到python提供了一个类来辅助with(类似auto_ptr的类库)
就是contextlib
包
contextlib
这个模块是标准库提供的环境管理器方面的工具类
- contextlib.contextmanager(func)
这是个装饰器
,用来生成context managers
对象,不需要生成class或者单独的__enter__
和__exit__
方法
一个简单的例子(和上面写的那个例子差不多似乎不是那么好用,但也不难用)
from contextlib import contextmanager
@contextmanager
def tag(name):
print "<%s>" % name
yield
print "</%s>" % name
>>> with tag("h1"):
... print "foo"
...
<h1>
foo
</h1>
- contextlib.nested(mgr1[, mgr2[, ...]])
组合多个context managers
对象到一个单独的nested context manager
对象
简单示例:
from contextlib import nested
with nested(*managers):
do_something()
python2.7后弃用,有语法可以实现
with A() as a, B() as b:
do_something()
-
contextlib.closing(thing)
返回Context Manager Type对象,thing需要有close函数
等同于下面的实现:
from contextlib import contextmanager@contextmanager def closing(thing): try: yield thing finally: thing.close()
例子如下:
from contextlib import closing import urllib with closing(urllib.urlopen('http://www.python.org')) as page: for line in page: print line
不需要显式的调用page.close(),即使是发生了错误,当离开with作用域的时候会自动调用page.close()。这个算得上是懒人的利器了。java里叫支持closable接口
网友评论