他能帮助我们,在打开一个资源的同时帮我们把资源清除掉
我们来敲一个小demo来看一下效果
import time
class A:
def __enter__(self):
print("enter")
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit")
with A():
time.sleep(5)
print("+++++++++")
time.sleep(5)
返回结果
现在我们可以知道当进入with语句调用def enter(self),然后执行print("++++++"),出执行语句调用def exit(self, exc_type, exc_val,exc_tb)
这就是上下文管理运用的两个方法 enter,exit
这里要注意:当单独调用A()时上下文管理的两个方法不会起到任何作用,只有使用with后才会产生效果
这里我抛出一个问题,下面代码f == a 返回的是True还是False?
import time
class A:
def __enter__(self):
print("enter")
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit")
f = A()
with f as a:
print(f == a)
运行代码我们发现返回的是False,a的值到底是什么那?我们分别打印一下f和a
import time
class A:
def __enter__(self):
print("enter") # 他的返回值将成为as子句后面变量的值
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit")
f = A()
with f as a:
print(f == a)
print(f)
print(a)
通过运行结果我们可以得到一个结论,a的值是上下文管理方法enter的返回值
class A:
def __enter__(self):
print("enter")
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type)
print(exc_val)
print(exc_tb)
print("exit")
with A() :
raise KeyError("Wrong")
运行结果:
总结以下几点内容:
注意点:如果__exit__方法返回的是一个等效为True的值,就会压制异常
class A:
def __enter__(self):
print("enter")
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type)
print(exc_val)
print(exc_tb)
return True # 等效为True
with A() :
raise KeyError("Wrong")
就可以看到没有红色的异常字体显示了
方法1.装饰器
import datetime
import time
import funtools
def logger(fn):
@funtools.wraps(fn) # wrapper = wrpas(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
end = (datetime.datetime.now()-start).total_seconds()
print(end)
return ret
return wrapper
@logger # add = logger(add)
def add(x:int,y:int):
time.sleep(2)
return x+y
add(5,4)
方法2.上下文管理
import datetime
import time
class TimeIt:
def __init__(self,fn,output=lambda fn,delta:print("{}:{}".format(fn.__name__,delta))):
self.fn = fn
self.output = output
def __enter__(self):
self.start = datetime.datetime.now()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now()-self.start)
self.output(self.fn,delta)
def __call__(self, x,y):
return self.fn(x,y)
def add(x:int,y:int):
time.sleep(2)
return x+y
with TimeIt(add) as obj:
obj(5,6)
进阶版本:用类实现装饰
import datetime
import time
class TimeIt:
def __init__(self,fn,output=lambda fn,delta:print("{}:{}".format(fn.__name__,delta))):
self.fn = fn
self.output = output
# 因为这里我们不能使用@wraps所以我们只能把装饰器转换成他的等价式
wraps(fn)(self) # @wraps(fn) wrapper=wraps(fn)(wrapper)
def __call__(self, x,y):
start = datetime.datetime.now()
ret = self.fn(x,y)
delta = (datetime.datetime.now()-start).total_seconds()
self.output(self.fn,delta)
return ret
@TimeIt # add = TimeIt(add)
def add(x:int,y:int):
time.sleep(2)
return x+y
add(4,6)
应用场景
扩展
了解即可,源代码中经常可见,要看了知道是做什么的
ctlr+鼠标右键进入源码
我们可以看到源码中很清楚的给了你一个经典的用法
from contextlib import contextmanager
@contextmanager
def a():
print("~~~~~~enter") # 相当于__enter__
try
yield 500 # 相当于__enter__,return的值
finally
print("-----exit") # 相当于__exit__
with a() as f:
print(f)
返回结果