python中with语句用起来很方便,那这后边的原理就是python中的上下文管理器。
1.什么是上下文管理器
上下文管理器
用来定义/控制代码块得执行前得准备工作,以及程序执行后得收尾工作。比如文件读写前的打开,与读写后的关闭。 网络请求的建立与断开等等。
下边拿requests.session()
为例:
import requests
with requests.session() as session:
response_html = session.get('http://www.baidu.com').text
print (response_html)
为什么with语句块能控制网络的建立与释放,原因是实现了上下文管理协议(context management protocol),相关的两个魔法方法:
__enter__(self)
: 不接收参数,定义上下文管理器在with语句块开头所执行的操作,其方法返回的值绑定到,
/as
后边所定义的变量。
__exit__(self, exc_type, exc_value, traceback)
:接收三个参数,exc_type 表示错误类型;exc_value 异常实例。有时会有参数传给异常构造方法,例如错误消息,这些参数可以使用exception_type获取。traceback:traceback对象。
session的上下文协议源码
2.创建上下文管理器的两种方式
我们重新实现一个类似open()的类,只是在创建文件对象时和关闭文件对象时增加了提示。
2.1 实现上下文协议
class File(object):
def __init__(self, file_path, mode):
self.file_path = file_path
self.mode = mode
def __enter__(self):
self.f = open(self.file_path, self.mode, encoding='utf-8')
print(f"{self.file_path} is open")
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
print(f"{self.file_path} is closed")
with File('test.txt', 'w') as f:
f.write('董小贱')
输出结果:
test.txt is open
test.txt is closed
2.2 装饰器实现
from contextlib import contextmanager
@contextmanager
def file(file_path, mode):
f = open(file_path, mode, encoding='utf-8')
print(f"{file_path} is open")
yield f
f.close()
print(f"{file_path} is closed")
with file('test.txt, 'w') as f:
f.write('董小贱')
输出结果:
test.txt is open
test.txt is closed
在使用 @contextmanager
装饰的生成器中,yield
语句的作用是把函数的定义体分成两部 分:yield
语句前面的所有代码在 with
块开始时(即解释器调用 __enter__
方法时)执行, yield
语句后面的代码在 with 块结束时(即调用 __exit__
方法时)执行。
其实这里有个问题,如果在with
块中抛出了一个异常,Python 解释器会将其捕获, 然后在 file 函数的 yield
表达式里再次抛出,但是代码中没有处理异常的代码,所以函数会终止,导致文件不能被关闭。所以我们要在yield
处处理异常。
所以第二版:
from contextlib import contextmanager
@contextmanager
def file(file_path, mode):
f = open(file_path, mode, encoding='utf-8')
print(f"{file_path} is open")
try:
yield f
except Exception as e:
print (e) # 处理异常的操作
finally:
f.close()
print(f"{file_path} is closed")
with file('24.txt', 'w') as f:
f.write('董小贱')
注:实现上下文管理协议,即可使用with
块。需要注意的是在 @contextmanager
装饰器装饰的生成器中,yield
与迭代没有任何关系。