Python with用法

来源:互联网 发布:ipad 知乎 编辑:程序博客网 时间:2024/06/07 04:56
一 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-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:
       执行 __enter__的内容
       执行 with_block.
  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')


运行结果:
enter
hello 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.这样要打开一个文件,处理它的内容,并且保证关闭它,你就可以简单地这样做:
     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函数的使用:

from contextlib import 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。 

2 0
原创粉丝点击