上下文处理器 是用来管理with语句 .与之对标的概念就是迭代器用来管理for语句.
Q: 上面提到了with语句的作用是什么?
A: with语句的目的是简化try/finally 模式,这种模式用于保证一段代码执行完毕后执行某操作,即使这段代码由于异常,return或者sys.exit()调用而终止.
上下文管理器的构成 上下文管理器协议包含有两个方法:__enter__方法,__exit__方法.
with语句开始运行 时,会在上下文管理器对象上调用__enter__方法.
with语句运行结束 后,会在上下文管理器对象上调用__exit__方法,扮演finally子句的作用.
示例 1 2 3 4 5 6 7 8 9 10 with open ("mirror.py" ) as fp: src = fp.read(60 ) print(len (src)) print(fp) print(fp.closed , fp.encoding) fp.read(60 )
**注意:**在第一行语句中分为两部分:
执行with后面的表达式得到的结果时上下文管理器对象 .
执行as语句,是此对象执行__enter__方法返回的结果.
open()方法本身提供的__enter__方法返回的是本身,所以可以看到fp变量指向的依旧是io.TextIPWrapper
不管控制流程以何种方式退出,都会在上下文管理器 对象上调用__exit__方法,而不是__enter__返回的方法.
深入理解上下文管理器的执行过程 像生成器的学习方式一样,我们既可以使用with语句隐式的调用上下文管理器的相关方法,也可以直接调用上下文管理器的相关方法以达到相同的目的.
定义一个上下文管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class LookingGlass : def __enter__ (self ): import sys self.original_write = sys.stdout.write sys.stdout.write = self.reverse_write return 'JABBERWOCKY' def reverse_write (self, text ): self.original_write(text[::-1 ]) def __exit__ (self, exc_type, exc_value, traceback ): import sys sys.stdout.write = self.original_write if exc_type is ZeroDivisionError: print('Please DO NOT divide by zero!' ) return True
通过定义__enter__与__exit__方法定义了一个上下文管理器,并且再次方法中利用monkey patch 对sys中的输出方法进行的反转输出的操作.
with语句使用上述的上下文管理器 1 2 3 4 5 6 7 8 9 10 11 12 from mirror import LookingGlasswith LookingGlass() as what: print("SUN QING ZHI" ) print(what) print(what) print("Back to Normal" )
可以看到上面的输出,在位于with语句内部时,print函数调用了上下文管理器中给定的逆输出函数,而跳出之后通过__exit__方法还原的原来的输出函数.
直接调用上下文管理器进行相同操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from mirror import LookingGlassmanager = LookingGlass() what = manager.__enter__() print("SUN QING ZHI" ) print(what) manager.__exit__(None , None ,None ) print(what) print("Back to Normal" )
手动调用了__enter__,__exit__ 两个方法,实现了相同的功能,这也就是with语句内部的逻辑.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from mirror import LookingGlasswith LookingGlass(): print("SUN QING ZHI" ) 1 /0 print("SUN QING ZHI" ) with LookingGlass(): print("SUN QING ZHI" ) A = NO_SUCH_NAME print("SUN QING ZHI" )
上面两个例子就是为了更好地理解上下文管理器的执行过程.
contextlib 模块中的提供的上下文管理器工具 应用到上下文管理器的场景其实很多,每次都通过手写管理器难免产生遗漏,为此官方提供了一系列的管理器,应用于不同场景.这里不一一的介绍里面的
@contextmanager : 上下文管理器装饰器,可以应用于生成器函数,将其装饰为一个上下文管理器.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 """ A "mirroring" ``stdout`` context manager. While active, the context manager reverses text output to ``stdout``:: # BEGIN MIRROR_GEN_DEMO_1 >>> from mirror_gen import looking_glass >>> with looking_glass() as what: # <1> ... print('Alice, Kitty and Snowdrop') ... print(what) ... pordwonS dna yttiK ,ecilA YKCOWREBBAJ >>> what 'JABBERWOCKY' # END MIRROR_GEN_DEMO_1 This exposes the context manager operation:: # BEGIN MIRROR_GEN_DEMO_2 >>> from mirror_gen import looking_glass >>> manager = looking_glass() # <1> >>> manager # doctest: +ELLIPSIS <contextlib._GeneratorContextManager object at 0x...> >>> monster = manager.__enter__() # <2> >>> monster == 'JABBERWOCKY' # <3> eurT >>> monster 'YKCOWREBBAJ' >>> manager # doctest: +ELLIPSIS >...x0 ta tcejbo reganaMtxetnoCrotareneG_.biltxetnoc< >>> manager.__exit__(None, None, None) # <4> >>> monster 'JABBERWOCKY' # END MIRROR_GEN_DEMO_2 """ import contextlib@contextlib.contextmanager def looking_glass (): import sys original_write = sys.stdout.write def reverse_write (text ): original_write(text[::-1 ]) sys.stdout.write = reverse_write yield 'JABBERWOCKY' sys.stdout.write = original_write
我这里就不一一总结了,了解就行.