1.what AND demo
我们希望把一些操作放到一个代码块中,这样在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态;所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”。
with 语句内置上下文管理工具。
with open("test/test.txt","w") as f: f.write("hello")
不使用上下文管理工具:
f = open("hl.txt", "w") print(f.closed) #运行到这里,会打印出False,因为没有关闭 f.write("Hello,context manager!") f.close() print(f.closed) #这里才会打印出True,表示文件关闭
如果这段代码根本执行不到f.close()这一句,就出现问题,这样就永远执行不到f.close()这一句,文件就永远不会被关闭。
可以用try…finally语句。
2.how自定义一个上下文管理器
上下文管理协议(Context Management Protocol):包含方法 __enter__() 和 __exit__(),支持该协议的对象要实现这两个方法。
上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了__enter__() 和 __exit__() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。
运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__() 和__exit__() 方法实现,
__enter__() 方法在语句体执行之前进入运行时上下文,
__exit__() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。
class MyMananger: # __enter__ 返回的对象会被with语句中as后的变量接受 def __enter__(self): print('connect to resource') return self def __exit__(self, exc_type, exc_value, tb): print('close resource conection') def query(self): print('query data')
with MyManager() as r:
r.query()
结果:
connect to resource
query data
close resource conection
3.contextlib中的装饰器contextmanager
demo1:
from contextlib import contextmanager class Mydoit: def doit(self): print('just do it') @contextmanager def make_contextmanager(): print('start to connect') yield Mydoit() print('end connect') pass
被装饰器装饰的函数分为三部分:
with语句中的代码块执行前执行函数中yield之前代码
yield返回的内容复制给as之后的变量
with代码块执行完毕后执行函数中yield之后的代码
with Mydoit() as r:
r.doit()
结果是:
start to connect
just do it
end connect
demo2:
#通过一个 yield 分为2个部分. #yield 前的代码 相当于 __enter__ , yield 后的代码相当于 __exit__ #而 yiled 产出的值 相当于 as 的变量上. import contextlib #装饰一下 @contextlib.contextmanager def open_file(filepath): #相当于 __enter__ print("准备打开") fileObject = open(filepath,'r') print("打开结束 , 准备yield") # fileObject 将赋值 给 as 后的变量 yield fileObject # 下面的代码相当于 __exit__ print("准备close") fileObject.close() print("close完成") with open_file("D:dex.txt") as fd: print("with 块进入了") time.sleep(3) print("完成操作")
demo3:
from contextlib import contextmanager
@contextmanager
def example():
l = [1,2,3,4]
print('start')
try:
# raise Exception('test')
yield l
finally:
print('end')
with example() as msg:
try:
for i in msg:
print(i)
except:
print(e.message)
start
1
2
3
4
end