什么是上下文管理器
上下文装饰器确保函数可以运行在正确的上下文中,或者是函数前后运行一些代码,上下文装饰器通常会被上下文管理器(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 error
class context:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
print("entering context!!!!")
self.f = open(self.filename, 'r')
return self.f
def __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 self
def __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 True
def operate(self):
1/0
with Resource() as res:
res.operate()
# ===connect to resource===
# ===close resource connection===
contextlib协议实现上下文管理器-管理资源
- 被装饰函数必须是一个生成器(有yield)
- yield之前的代码相当于 enter
- yield之后的代码相当于 exit
import contextlib
@contextlib.contextmanager
def 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.contextmanager
def open_func1(file_name):
"""能实现上下文管理器的第一个目的(管理资源) + 处理异常"""
print("open file: ", file_name, "in __enter__")
file_handler = open(file_name, "r")
try:
yield file_handler
except BaseException as exc:
print("the exception was: ", exc)
finally:
print("close file: ", file_name, "in __exit__")
file_handler.close()
return
with open_func1("test1.py") as f:
for line in f:
1/0
print(line)
# open file: test1.py in __enter__
# the exception was: division by zero
# close file: test1.py in __exit__