什么是上下文管理器
上下文装饰器确保函数可以运行在正确的上下文中,或者是函数前后运行一些代码,上下文装饰器通常会被上下文管理器(with)替代。
为了确保即使出现错误的情况也能运行某些清理代码,try…finally语句是很有用的,例如以下使用场景:
关闭一个文件
释放一个锁
创建一个临时的代码补丁
工作原理
任何实现了上下文管理器协议的对象都可以用做上下文管理器,协议包含两个特殊方法:
_enter(self):
_exit(self, exc_type, exc_value, traceback):
with语句执行过程如下:
调用enter方法,任何返回值都会绑定到指定的as子句
执行内部代码块
调用exit方法
class ContextIlustrations:def __enter__(self):print("entering context")def __exit__(self, exc_type, exc_val, exc_tb):print("leaving coontext")if exc_type is None:print("with no error")else:print("with an error {}".format(exc_val))if __name__ == '__main__':with ContextIlustrations():print("inside")------------------------ --------------#entering context#inside#leaving coontext#with no errorclass context:def __init__(self, filename):self.filename = filenamedef __enter__(self):print("entering context!!!!")self.f = open(self.filename, 'r')return self.fdef __exit__(self, exc_type, exc_val, exc_tb):print("leaving context")self.f.close()test = context('get_dept_id.txt')with test as t:print("test resutl: {}".format(t))------------------------#entering context!!!!#test resutl: <_io.TextIOWrapper name='get_dept_id.txt' mode='r' encoding='cp936'>#leaving context
实现通用语法
with context_manager:# 如果提供了上下文变量,可以用as子句保存为临时变量:with context_magager as context:# 如果多个上下文管理器同时使用:with A() as a, B() as b:...#这种写法等同于:with A() as a:with B() as b:...
通过上下文管理器处理异常
class Resource():def __enter__(self):print('===connect to resource===')return selfdef __exit__(self, exc_type, exc_type, exc_tb):"""exc_type: 异常类型exc_type: 异常值exc_tb: 异常的错误栈信息"""# 异常可以在 __exit__ 进行捕获并由你自己决定如何处理,# 是抛出呢还是在这里就解决了。在 __exit__ 里返回 True (# 没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。print('===close resource connection===')return Truedef operate(self):1/0with Resource() as res:res.operate()# ===connect to resource===# ===close resource connection===
contextlib协议实现上下文管理器-管理资源
- 被装饰函数必须是一个生成器(有yield)
 - yield之前的代码相当于 enter
 - yield之后的代码相当于 exit
 
import contextlib@contextlib.contextmanagerdef open_func(file_name):"""能实现上下文管理器的资源管理"""# __enter__方法print("open file: ", file_name, "in __enter__")file_handler = open(file_name, "r")yield file_handler# __exit__ 方法print("close file: ", file_name, "in __exit__")file_handler.close()with open_func("test1.py") as f:for line in f:print(line)
contextlib协议实现上下文管理器-管理资源+异常处理
import contextlib@contextlib.contextmanagerdef open_func1(file_name):"""能实现上下文管理器的第一个目的(管理资源) + 处理异常"""print("open file: ", file_name, "in __enter__")file_handler = open(file_name, "r")try:yield file_handlerexcept BaseException as exc:print("the exception was: ", exc)finally:print("close file: ", file_name, "in __exit__")file_handler.close()returnwith open_func1("test1.py") as f:for line in f:1/0print(line)# open file: test1.py in __enter__# the exception was: division by zero# close file: test1.py in __exit__
