常见的形式
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: 调用栈回溯对象,封装了异常发生时的调用栈信息