2.1.3.2 使用生成器定义上下文管理器
当讨论生成器时,曾说过与循环相比,我们更偏好将生成器实现为一个类,因为,他们更短、更美妙,状态存储在本地,而不是实例和变量。另一方面,就如在双向沟通中描述的,生成器和它的调用者之间的数据流动可以是双向的。这包含异常,可以在生成器中抛出。我们希望将上下文生成器实现为一个特殊的生成器函数。实际上,生成器协议被设计成可以支持这个用例。
In [ ]:
@contextlib.contextmanager
def some_generator(<arguments>):
<setup>
try:
yield <value>
finally:
<cleanup>
contextlib.contextmanager帮助者可以将一个生成器转化为上下文管理器。生成器需要遵循一些封装器函数强加的规则—它必须yield
一次。在yield
之前的部分是从enter
来执行,当生成器在yield
挂起时,由上下文管理器保护的代码块执行。如果抛出异常,解释器通过exit
参数将它交给封装器,然后封装器函数在yield
语句的点抛出异常。通过使用生成器,上下文管理器更短和简单。
让我们将closing
例子重写为一个生成器:
In [ ]:
@contextlib.contextmanager
def closing(obj):
try:
yield obj
finally:
obj.close()
让我们将assert_raises
例子重写为生成器:
In [ ]:
@contextlib.contextmanager
def assert_raises(type):
try:
yield
except type:
return
except Exception as value:
raise AssertionError('wrong exception type')
else:
raise AssertionError('exception expected')
这里我们使用修饰器来将一个生成器函数转化为上下文管理器!
In [1]:
%matplotlib inline
import numpy as np