Python

环境管理器with

2016-07-17  本文已影响42人  MrHamster

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

定义

来个例子, 实现个普遍适用的管理器,省去每次使用都需要定义一个类

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

这个模块是标准库提供的环境管理器方面的工具类

      from contextlib import contextmanager
    
      @contextmanager
      def tag(name):
          print "<%s>" % name
          yield
          print "</%s>" % name

      >>> with tag("h1"):
      ...    print "foo"
      ...
      <h1>
      foo
      </h1>
from contextlib import nested
with nested(*managers): 
    do_something()

python2.7后弃用,有语法可以实现

with A() as a, B() as b:
    do_something()

不需要显式的调用page.close(),即使是发生了错误,当离开with作用域的时候会自动调用page.close()。这个算得上是懒人的利器了。java里叫支持closable接口

End

上一篇 下一篇

猜你喜欢

热点阅读