Python 上下文管理器

已发布 2017-12-28 15:28:58

常见的形式

with open('abc.txt', 'r') as f:
    print f.read()

with 工作的方式是利用了对象(python里一切皆对象)里定义的__enter____exit__方法。

当进入with区域的时候,调用__enter__方法,当退出这个区域的时候,保证__exit__一定会被调用。

相当于

def with(obj):
    try:
        f = obj.__enter__()
        # do something using f
    finally:
        obj.__exit__()

Why

这种形式保证了__exit__一定会被调用,经常用在资源管理的场景中,比如关闭打开的文件。

contextlib

Python3.2 新增了contextlib这个标准库,它下面有一个叫ContextDecorator的类。

继承它并覆盖__enter____exit__方法的话,子类的实例就可以用来装饰其他方法。

from contextlib import ContextDecorator

class makeparagraph(ContextDecorator):
    def __enter__(self):
        print('<p>')
        return self

    def __exit__(self, *exc):
        print('</p>')
        return False

@makeparagraph()
def emit_html():
    print('Here is some non-HTML')

emit_html()

输出效果为

<p>
Here is some non-HTML
</p>

contextdecorator

对于低版本的Python,发现了一个第三方库实现了类似功能。

核心部分实现代码为:

_NO_EXCEPTION = (None, None, None)

class ContextDecorator(object):
    "A base class or mixin that enables context managers to work as decorators."

    def __call__(self, f):
        @wraps(f)
        def inner(*args, **kw):
            self.__enter__()  # 在被装饰的函数前调用 enter

            exc = _NO_EXCEPTION
            try:
                result = f(*args, **kw)
            except Exception:
                exc = sys.exc_info()

            catch = self.__exit__(*exc)  # 在被装饰的函数后调用 exit

            if not catch and exc is not _NO_EXCEPTION:
                _reraise(*exc)
            return result
        return inner

Exception三元组

上面的__exit__()应该传入的是三个参数,代表了with语句中执行过程的异常信息。

这个异常信息可以通过sys.exc_info()来获取。这三个值分别是:

  • type: 异常类型,Exception的子类
  • value: 异常类型的实例
  • traceback: 调用栈回溯对象,封装了异常发生时的调用栈信息

References

comments powered by Disqus