Python之contextlib模块的使用
contextlib模块的作用
用于处理上下文管理器和with语句,上下文管理器:负现管理一个代码块中的资源,会在处理代码块时创建资源,然后在退出代码块后清理这个资源。
例如:文件就是支持上下文管理器API,可以确认完成文件读写后关闭文件。
1、文件操作的with,使用到contextlib的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
with open('pymotw.txt', 'wt') as f: f.write('contents go here')
运行效果
2、__init__、__enter__、__exit__上下文管理器的使用示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Context(object): def __init__(self): print('__init__') def __enter__(self): print('__enter__') return self def __exit__(self, exc_type, exc_val, exc_tb): print('__exit__()') with Context(): print('Doing work in the context.')
运行效果
__init__ __enter__ Doing work in the context. __exit__()
3、__init__、__enter__、__exit__、__del__上下文管理器类对象相互调用的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class WithinContext: def __init__(self, context): print('WithinContext.__init__({})'.format(context)) def do_something(self): print('WithinContext.do_something()') def __del__(self): print('WithinContext.__del__') class Context: def __init__(self): print('Context.__init__()') def __enter__(self): print('Context.__enter__()') return WithinContext(self) def __exit__(self, exc_type, exc_val, exc_tb): print('Context.__exit__()') with Context() as c: c.do_something()
运行效果
Context.__init__() Context.__enter__() WithinContext.__init__(<__main__.Context object at 0x00000299A5434048>) WithinContext.do_something() Context.__exit__() WithinContext.__del__ #总结 #对象的销毁是在__exit__之后
4、__exit__上下文管理器类异常错误的处理示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Context: def __init__(self, handle_error): print('__init__({})'.format(handle_error)) self.handle_error = handle_error def __enter__(self): print('__enter__()') return self def __exit__(self, exc_type, exc_val, exc_tb): print('__exit__()') print(' exc_type =', exc_type) print(' exc_val =', exc_val) print(' exc_tb =', exc_tb) return self.handle_error with Context(True): raise RuntimeError('error message handled') print() with Context(False): raise RuntimeError('error message propagated')
运行效果
__init__(True) __enter__() __exit__() exc_type = <class 'RuntimeError'> exc_val = error message handled exc_tb = <traceback object at 0x0000021B90F9DE48> __init__(False) __enter__() __exit__() exc_type = <class 'RuntimeError'> exc_val = error message propagated exc_tb = <traceback object at 0x0000021B90F9DDC8> Traceback (most recent call last): File "D:/Program Files/JetBrains/PyCharmData/PythonStudent/tornado_Test/server.py", line 25, in <module> raise RuntimeError('error message propagated') RuntimeError: error message propagated #总结: 当__exit__返回False的时候,会报异常错误
5、利用contextlib模块,实现上下文管理器的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib class Context(contextlib.ContextDecorator): def __init__(self, how_used): self.how_used = how_used print('__init__({})'.format(how_used)) def __enter__(self): print('__enter__({})'.format(self.how_used)) return self def __exit__(self, exc_type, exc_val, exc_tb): print('__exit__({})'.format(self.how_used)) # 装饰器,实现上下文管理器运行 @Context('as decorator') def func(message): print(message) print() func('Doing work in the wrapped function') print() # with 实现上下文管理器运行 with Context('as context manager'): print('Doing work in the context')
运行效果
__init__(as decorator) __enter__(as decorator) Doing work in the wrapped function __exit__(as decorator) __init__(as context manager) __enter__(as context manager) Doing work in the context __exit__(as context manager)
#总结
contextlib模块实现装饰器运行上下文管理器,不会影响到传统的with的使用
6、@contextlib.contextmanager,将一个生成器函数转为上下文管理器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib @contextlib.contextmanager def make_context(): print(' entering') try: yield {} except RuntimeError as err: print(' ERROR:', err) finally: print(' exiting') print('Normal:') with make_context() as value: print(' inside with statement:', value) print('\nHandled error:') with make_context() as value: raise RuntimeError('showing example of handling an error') print('\nUnhandled error:') with make_context() as value: raise ValueError('this exception is not handled')
运行效果
Normal: entering inside with statement: {} exiting Handled error: entering ERROR: showing example of handling an error exiting Unhandled error: entering exiting Traceback (most recent call last): File "D:/Program Files/JetBrains/PyCharmData/PythonStudent/tornado_Test/server.py", line 25, in <module> raise ValueError('this exception is not handled') ValueError: this exception is not handled
7、忽略异常的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib class NonFatalError(Exception): pass def non_idempotent_operation(): raise NonFatalError( 'The operation failed because of existing state' ) try: print('trying non-idempotent operation') non_idempotent_operation() print('succeeded!') except NonFatalError: pass print('done')
运行效果
trying non-idempotent operation
done
8、利contextlib.suppress()抑制with抛出的异常示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib class NonFatalError(Exception): pass def non_idempotent_operation(): raise NonFatalError( 'The operation failed because of existing state' ) with contextlib.suppress(NonFatalError): print('trying non-idempotent operation') non_idempotent_operation() print('succeeded!') print('done')
运行效果
trying non-idempotent operation
done
9、重定向标准输出流,错误输出流示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from contextlib import redirect_stdout, redirect_stderr import io import sys def misbehaving_function(a): sys.stdout.write('(stdout) A: {!r}\n'.format(a)) sys.stderr.write('(stderr) A: {!r}\n'.format(a)) capture = io.StringIO() with redirect_stdout(capture), redirect_stderr(capture): misbehaving_function(5) print(capture.getvalue())
运行效果
(stdout) A: 5
(stderr) A: 5
10、上下文管理器入栈,即(遇到循环的时候,代码一直排队运行,等到退出的时候,再排队退出)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib @contextlib.contextmanager def make_context(i): print('{} entering'.format(i)) yield {} print('{} exiting'.format(i)) def variable_stack(n, msg): with contextlib.ExitStack() as stack: for i in range(n): stack.enter_context(make_context(i)) print(msg) variable_stack(2, 'inside context')
运行效果
0 entering 1 entering inside context 1 exiting 0 exiting
11、上下文管理器类展示错误是如何传播的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib class Tracker(object): "Base class for noisy context managers." def __init__(self, i): self.i = i def msg(self, s): print(' {}({}): {}'.format( self.__class__.__name__, self.i, s)) def __enter__(self): self.msg('entering') class HandleError(Tracker): "如果收到异常,请将其视为已处理." def __exit__(self, *exc_details): received_exc = exc_details[1] is not None if received_exc: self.msg('handling exception {!r}'.format( exc_details[1])) self.msg('exiting {}'.format(received_exc)) # 返回指示是否异常的布尔值 # 已经处理好了. return received_exc class PassError(Tracker): "如果收到异常,则传播它." def __exit__(self, *exc_details): received_exc = exc_details[1] is not None if received_exc: self.msg('passing exception {!r}'.format( exc_details[1])) self.msg('exiting') # 返回false,表示未处理任何异常。 return False class ErrorOnExit(Tracker): "引起异常." def __exit__(self, *exc_details): self.msg('throwing error') raise RuntimeError('from {}'.format(self.i)) class ErrorOnEnter(Tracker): "引起异常." def __enter__(self): self.msg('throwing error on enter') raise RuntimeError('from {}'.format(self.i)) def __exit__(self, *exc_info): self.msg('exiting')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib from contextlib_context_managers import * def variable_stack(contexts): with contextlib.ExitStack() as stack: for c in contexts: stack.enter_context(c) print('在堆栈之外,任何错误都被处理') print('没有错误:') variable_stack([ HandleError(1), PassError(2), ]) print('\n上下文堆栈末尾的错误:') variable_stack([ HandleError(1), HandleError(2), ErrorOnExit(3), ]) print('\n上下文堆栈中间的错误:') variable_stack([ HandleError(1), PassError(2), ErrorOnExit(3), HandleError(4), ]) try: print('\n错误忽略:') variable_stack([ PassError(1), ErrorOnExit(2), ]) except RuntimeError: print('error handled outside of context')
运行效果
没有错误: HandleError(1): entering PassError(2): entering PassError(2): exiting HandleError(1): exiting False 在堆栈之外,任何错误都被处理 上下文堆栈末尾的错误: HandleError(1): entering HandleError(2): entering ErrorOnExit(3): entering ErrorOnExit(3): throwing error HandleError(2): handling exception RuntimeError('from 3') HandleError(2): exiting True HandleError(1): exiting False 在堆栈之外,任何错误都被处理 上下文堆栈中间的错误: HandleError(1): entering PassError(2): entering ErrorOnExit(3): entering HandleError(4): entering HandleError(4): exiting False ErrorOnExit(3): throwing error PassError(2): passing exception RuntimeError('from 3') PassError(2): exiting HandleError(1): handling exception RuntimeError('from 3') HandleError(1): exiting True 在堆栈之外,任何错误都被处理 错误忽略: PassError(1): entering ErrorOnExit(2): entering ErrorOnExit(2): throwing error PassError(1): passing exception RuntimeError('from 2') PassError(1): exiting error handled outside of context 总结: 如果栈中的任何上下文管理吕接收到一个异常并返回一个True值,那么这会阻止该异常继续向上传播到任何其他上下文管理器。
12、任意上下文管理的回调示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib def callback(*args, **kwds): print('closing callback({}, {})'.format(args, kwds)) with contextlib.ExitStack() as stack: stack.callback(callback, 'arg1', 'arg2') stack.callback(callback, arg3='val3')
运行效果
closing callback((), {'arg3': 'val3'}) closing callback(('arg1', 'arg2'), {})
13、任意上下文管理的回调异常的处理示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib def callback(*args, **kwds): print('closing callback({}, {})'.format(args, kwds)) try: with contextlib.ExitStack() as stack: stack.callback(callback, 'arg1', 'arg2') stack.callback(callback, arg3='val3') raise RuntimeError('thrown error') except RuntimeError as err: print('ERROR: {}'.format(err))
运行效果
closing callback((), {'arg3': 'val3'}) closing callback(('arg1', 'arg2'), {}) ERROR: thrown error
总结:
由于回调不能访问错误,所以它们无法防止异常在上下文管理器栈中继续传播
14、装饰器实现上下文管理器的回调
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib with contextlib.ExitStack() as stack: @stack.callback def inline_cleanup(): print('inline_cleanup()') print('local_resource = {!r}'.format(local_resource)) local_resource = 'resource created in context' print('within the context')
运行效果
within the context inline_cleanup() local_resource = 'resource created in context'
15、部分栈的清理的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import contextlib from contextlib_context_managers import * def variable_stack(contexts): with contextlib.ExitStack() as stack: for c in contexts: stack.enter_context(c) # 返回新堆栈的close()方法作为清理 return stack.pop_all().close # 显式返回None,表示无法干净地初始化ExitStack,但已经发生了清除 return None print('没有错误:') cleaner = variable_stack([ HandleError(1), HandleError(2), ]) cleaner() print('\n处理错误构建上下文管理器堆栈:') try: cleaner = variable_stack([ HandleError(1), ErrorOnEnter(2), ]) except RuntimeError as err: print(' 捕捉错误 {}'.format(err)) else: if cleaner is not None: cleaner() else: print('no cleaner returned') print('\n未处理的错误构建上下文管理器堆栈:') try: cleaner = variable_stack([ PassError(1), ErrorOnEnter(2), ]) except RuntimeError as err: print('捕捉错误 {}'.format(err)) else: if cleaner is not None: cleaner() else: print('没有清洁工回来')
运行效果
没有错误: HandleError(1): entering HandleError(2): entering HandleError(2): exiting False HandleError(1): exiting False 处理错误构建上下文管理器堆栈: HandleError(1): entering ErrorOnEnter(2): throwing error on enter HandleError(1): handling exception RuntimeError('from 2') HandleError(1): exiting True no cleaner returned 未处理的错误构建上下文管理器堆栈: PassError(1): entering ErrorOnEnter(2): throwing error on enter PassError(1): passing exception RuntimeError('from 2') PassError(1): exiting 捕捉错误 from 2