一 with介绍
with从Python 2.5就有,需要from __future__ import with_statement,但自python 2.6开始,成为默认关键字。在What‘s
new in python2.6/3.0中,明确提到:
The ‘with‘
statement is a control-flow structure whose basicstructure is:
with expression [as variable]:
with expression [as variable]:
with-block
也就是说with是一个控制流语句,跟if/for/while/try之类的是一类的,with可以用来简化try finally代码,看起来可以比try finally更清晰。
这里新引入了一个"上下文管理协议"context management protocol“,实现方法是为一个类定义__enter__和__exit__两个函数。with expresion as variable的执行过程是,首先执行__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行__exit__函数。
这样的过程其实等价于:
try:
这里新引入了一个"上下文管理协议"context management protocol“,实现方法是为一个类定义__enter__和__exit__两个函数。with expresion as variable的执行过程是,首先执行__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行__exit__函数。
这样的过程其实等价于:
try:
执行 __enter__的内容
执行 with_block.
finally:
执行 __exit__内容
finally:
执行 __exit__内容
只不过,现在把一部分代码封装成了__enter__函数,清理代码封装成__exit__函数。
二 代码实现
二 代码实现
class echo :
def output(self) :
print ‘hello world‘
def __enter__(self):
print ‘enter‘
return self
def __exit__(self, exception_type, exception_value, exception_traceback):
print ‘exit‘
if exception_type == ValueError :
return True
else:
return False
with echo() as e:
e.output()
print ‘do something inside‘
print ‘-----------‘
with echo() as e:
raise ValueError(‘value error‘)
print ‘-----------‘
with echo() as e:
raise Exception(‘can not detect‘)
运行结果:
enterhello world
do something inside
exit
-----------
enter
exit
-----------
enter
exit
Traceback (most recent call last):
File "exception.py", line 102, in <module>
raise Exception(‘can not detect‘)
Exception: can not detect
注意:
1,e不是test()的值,test()返回的是"context manager object",是给with用的。e获得的是__enter__函数的返回值,这是with拿到echo()的对象执行之后的结果.
2,__exit__函数的返回值用来指示with-block部分发生的异常是否要re-raise,如果返回False,则会re-raise with-block的异常,如果返回True,则就像什么都没发生。
1,e不是test()的值,test()返回的是"context manager object",是给with用的。e获得的是__enter__函数的返回值,这是with拿到echo()的对象执行之后的结果.
2,__exit__函数的返回值用来指示with-block部分发生的异常是否要re-raise,如果返回False,则会re-raise with-block的异常,如果返回True,则就像什么都没发生。
三 使用环境
1.这样要打开一个文件,处理它的内容,并且保证关闭它,你就可以简单地这样做:
with open("x.txt") as
f:
data = f.read()
do something with data
2.Flask中初始化数据库的代码:
def
init_db():
with app.app_context():
db = get_db()
with app.open_resource(‘shema.sql‘, mode=‘r‘) as f:
db.cursor().executescript(f.read())
db.commit()
3.contextlib是为了加强with语句,提供上下文机制的模块,它是通过Generator实现的。通过定义类以及写__enter__和__exit__来进行上下文管理虽然不难,但是很繁琐。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制。常用框架如下:
from contextlib import contextmanager
@contextmanager
def make_context() :
print ‘enter‘
try :
yield {}
except RuntimeError, err :
print ‘error‘ , err
finally :
print ‘exit‘
with make_context() as value :
print value
contextlib还有连个重要的东西,一个是nested,一个是closing,前者用于创建嵌套的上下文,后则用于帮你执行定义好的close函数。但是nested已经过时了,因为with已经可以通过多个上下文的直接嵌套了。下面是一个例子:
from
contextlib import contextmanager
from
contextlib import nested
from
contextlib import closing
@contextmanager
def
make_context(name) :
print ‘enter‘, name
yield name
print ‘exit‘, name
with
nested(make_context(‘A‘), make_context(‘B‘)) as (a, b) :
print a
print b
with
make_context(‘A‘) as a, make_context(‘B‘) as b :
print a
print b
class
Door(object) :
def open(self) :
print ‘Door is opened‘
def close(self) :
print ‘Door is closed‘
with
closing(Door()) as door :
door.open()
运行结果:
closing函数的使用:
import urllib
with closing(urllib.urlopen(‘http://www.python.org‘)) as page:
for line in page:
print line
四 小结
python有很多强大的特性,由于我们平常总习惯于之前C++或java的一些编程习惯,时常忽略这些好的机制。因此,要学会使用这些python特性,让我们写的python程序更像是python。