从with关键字到编写自己简单的ContextManager(二)
接上文
contextlib.contextmanager的用法是怎样的?我摘抄一下模块源代码
class MyGeneratorContextManager(object): def __init__(self, gen): print("__init__ called") self.gen = gen def __enter__(self): print("__enter__ called") return self.gen.next() def __exit__(self, exc_type, exc_val, exc_tb): print("__exit__called exc_type = %s, exc_val = %s, exc_tb = %s"\ % (exc_type, exc_val, exc_tb)) # 这里没有做异常处理,需要处理StopIteration异常 # 不是用return也可以 # 下面这句话将输出yield [1, 2, 3]后面的的打印语句end foo return self.gen.next() def MyContextManager(func): def tmpf(*args): print("func info:", func) return MyGeneratorContextManager(func(*args)) return tmpf@MyContextManagerdef foo(val): # 尝试用老方法捕捉错误 try: print("start foo", val) yield [1, 2, 3] # 下面一行需要调用self.gen.next()才能输出 print("end foo") except (Exception, AssertionError): # 但是实际上并没有捕捉到yield中的错误 # except的功能完全被__exit__取代 print("EXCEPTION ENCOUNTERED!") finally: print("FINALLY")print("foo is ", foo)print("foo() is ", foo("bbbb"))print("\nWITH INFO BELOW:")with foo("aaaa") as tmp: print("START WITH") #: tmp实际上就是yield穿过来的值 print(tmp) for i in tmp: print(i) assert 1>2 # 出错之后直接从with中跳出去,下面不可能被执行 print("END WITH")
输出结果是:
# 首先可以看到foo的值是闭包中的tmpf函数('foo is ', <function tmpf at 0x7fb78b15f140>)('func info:', <function foo at 0x7fb78b15f0c8>)__init__ called('foo() is ', <__main__.MyGeneratorContextManager object at 0x7fb78b1591d0>)WITH INFO BELOW:# 请看,两次调用foo(),发现他们最终都是同一个foo函数('func info:', <function foo at 0x7fb78b15f0c8>)# 但是奇怪的是,函数被初始化了两次?这是因为这是个工厂模式,每次调用的函数虽然一样,但是会生成不同的类__init__ called__enter__ called('start foo', 'aaaa')START WITH[1, 2, 3]123# assert触发的错误没有被except捕捉到!被__exit__函数捕捉到了__exit__called exc_type = <type 'exceptions.AssertionError'>, exc_val = , exc_tb = <traceback object at 0x7fb78b15c2d8>end fooFINALLY# 为什么跳出StopIteration异常?这是因为gen.next()已经走到头了,我们没有处理这异常Traceback (most recent call last): File "/home/leonardo/Aptana Studio 3 Workspace/PythonStudy/src/thinking/mycontext_manager.py", line 68, in <module> print("END WITH") File "/home/leonardo/Aptana Studio 3 Workspace/PythonStudy/src/thinking/mycontext_manager.py", line 31, in __exit__ return self.gen.next()StopIterationdef tmpf(*args): print("func info:", func) return MyGeneratorContextManager(func(*args))return self.gen.next()