上下文管理器
一、基本语法
with EXPR as VAR
pass
- 其中的EXPR是一个表达式,返回的是一个对象,var用来保存EXPR表达式返回的对象,可以有单个或者多个返回值。
>>>以最常用的with为例:
-
with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的清理操作,释放资源,比如,文件使用后的自动关闭、线程中锁的自动获取和释放等;
-
with语句即
上下文管理器
,含有__enter__
和__exit__
方法的对象都是上下文管理器;- enter:在执行with语句之前,首先执行该方法,通常返回一个实例对象,如果with语句有as目标,则将对象赋值给as目标;
- exit:在执行with语句结束后,自动调用__exit__方法,用户释放资源,若此方法返回布尔值为True,程序会忽略异常。(什么时候能返回True)
-
操作文本对象的时候,几乎所有的人都会让我们要用
with open
,这就是一个上下文管理器的一种。比如:
with open('1.txt') as f:
print(f.readllines())
输出结果:
'print'
'True'
- 表达式open(‘1.txt’)返回是一个_io.TextIOWrapper 类型的变量用f接受到。在with语句块中就可以使用这个变量操作文件。执行with这个结构之后。f会自动关闭。相当于自带了一个finally。
- 但是with本身并没有异常捕获的功能,如果发生了运行时异常的情况,它照样可以关闭文件释放资源。
另外:
try:
with open('1.txt') as f2:
print(f2.read())
f2.seek(-5,os.SEEK_SET)
except ValueError as e:
print("error")
print(f2.closed)
输出结果:
'print'
'error'
'True'
二、如何写上下管理器
-
要自己实现这样一个上下文管理,要先知道上下文管理协议。
-
简单点说,就是在一个类里,实现了__enter__和__exit__的方法
-
实现这个类的示例:
class Resource()
def __enter__(self):
# 连接资源
print('==connect to resource==')
def __exit__(self):
# 关闭资源连接
print('==close resource connection==')
def operate(self):
# 运行中
print('==in operation==')
if __name__ == '__main__':
with Resource() as res:
res.operate()
- 输出结果:
1.'==connect to resource=='
2.'==in operation=='
3.'==close resource connection=='
从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在__enter__中,而将资源的关闭写在__exit__中。
三、为什么要用资源管理器
- 在处理异常的时候,通常会使用
try..except...
来捕获异常。这样做一个不好的地方是,在代码的主程序中,会有大量的异常处理,这会很大的影响我们的可读性。 - 好的一点做法是,可以使用
with
将异常的处理隐藏起来。 - 仍然是以上面的代码为例,我们将
1/0
这个一定会抛出异常的代码
写在operate
class Resource()
def __enter__(self):
# 连接资源
print('==connect to resource==')
def __exit__(self):
# 关闭资源连接
print('==close resource connection==')
def operate(self):
# 运行中
1/0
with Resource() as res:
res.operate()
- 运行一下会发现,居然不会报错。
- 这就是上下文管理协议的强大之处,异常可以在__exit__进行捕获,并由你自己决定如何处理,是抛出呢?还是在这里解决呢?在__exit__里返回
True
(没有return,就默认为return False),就相当于告诉 Python 解释器,这个异常我们已经捕获了,不需要再往外抛了。 - 在写
__exit__
函数时,需要注意的是,他必须有三个参数:- 1.exc_type:异常类型
- 2.exc_val:异常值
- 3.exc_tb:异常的错误栈信息
- 当主程序代码没有报异常时,这三个参数都将为None。