通过生成器可以很容易地构造出一个context manager对象。看下面的代码。
class MyContextManager:
def __init__(self, gen):
self.gen = gen
def __enter__(self):
print "Entering context manager __enter__ method"
return self.gen.next()
def __exit__(self, exc_t, exc_v, traceback):
print "Entering context manager __exit__ method"
self.gen.close()
上面定义的MyContextManager类在构造时传入了一个生成器对象。而__enter__方法执行生成器对象的next方法,__exit__方法执行生成器对象的close方法。这样,context manager对象的逻辑完全在生成器对象方法中定义。从而,这里定义的MyContextManager类可以作为一个通用的类使用。
下面是一个简单生成器方法。
def simple_gen():
print "Entering generator"
yield "yield result"
print "Exiting generator"
下面的with语句中利用生成器构造了一个context manager对象。
with MyContextManager(simple_gen()) as yr:
print yr
在上面代码的第1行,分别调用context manager对象的__init__和__enter__方法。在__enter__方法调用了生成器对象的next方法。而生成器对象next方法的返回值是yield关键字后面表达式的值,也即字符串"yield result"。这个值也是生成器对象__enter__方法的返回值。这个返回值,最终传给了with语句中定义的yr变量。
下面是代码的输出。
Entering context manager __enter__ method
Entering generator
yield result
Entering context manager __exit__ method
注意:生成器对象方法的最后一个print语句并没有被执行。这是因为,当执行context manager对象的__exit__方法时,调用了生成器对象方法的close方法。而该方法的实现机制是在yield语句出抛出一个GeneratorExit的异常。这样,yield语句后面的语句当然不被执行。