文件IO操作可以对文件对象使用上下文管理,使用with…as语法 上下文管理对象
方法意义__enter__进入与此对象相关的上下文.如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上__exit__退出与此对象相关的上下文实例化对象的时候,并不会调用enter,进入with语句块调用__enter__方法,然后执行语句体,最后离开with语句块的时候,调用__exit__方法 with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作 注意,with并不开启一个新的作用域 上下文管理的安全性
class Point: def __init__(self): print('init') def __enter__(self): print("enter") def __exit__(self,exc_type,exc_val,exc_tb): print('exit') f = Point() with f as p: import sys sys.exit(1) print(f==p)从执行结果来看,依然执行__exit__函数,哪怕是退出python运行环境 说明上下文管理很安全
问题在__enter__方法上,它将自己的返回值赋给f,修改上例
with语法,会调用with后的对象__enter__方法,如果有as,则将该方法的返回值赋给as子句的变量 上例,可以等价为f = p.__enter__() 方法的参数 enter,没有其他参数 exit,方法有三个参数(self,exc_type,exc_val,exc_tb) 这三个参数都与异常有关 如果该上下文退出时没有异常,这三个参数都为None 如果有异常,参数意义如下 exc_type,异常类型 exc_val,异常的值 exc_tb(traceback),异常的追踪信息 __exit__方法返回一个等效True的值,则压制异常;否则,继续抛出异常
练习 为加法函数计时
使用装饰器显示该函数的执行时长使用上下文管理来显示该函数的执行时长 import datetime import time from functools import wraps,update_wrapper def lagger(fn): @wraps(fn) def warrapr(*args,**kwargs): statr = datetime.datetime.now() ret = fn(*args,**kwargs) datel = '{}s'.format((datetime.datetime.now()-statr).total_seconds()) print(datel) return ret return warrapr @lagger def add(x,y): """12312""" time.sleep(2) return x+y print(add(10,10)) class Point: """abddadss""" def __init__(self,fn): self.fn = fn update_wrapper(self,fn) # self.__name__ = fn.__name__ # self.__doc__ = fn.__doc__ def __enter__(self): self.start = datetime.datetime.now() return self.fn def __exit__(self, exc_type, exc_val, exc_tb): delta = (datetime.datetime.now()-self.start).total_seconds() print('{}s'.format(delta)) with Point(add) as f: print(add(4,5))用类当字符串
import datetime import time from functools import wraps,update_wrapper class Point: """abddadss""" def __init__(self,fn): self.fn = fn update_wrapper(self,fn) #文档字符串 # self.__name__ = fn.__name__ # self.__doc__ = fn.__doc__ def __enter__(self): self.start = datetime.datetime.now() return self.fn def __exit__(self, exc_type, exc_val, exc_tb): delta = (datetime.datetime.now()-self.start).total_seconds() # print(delta) # print(exc_tb) # print(exc_type) # print(exc_val) # print("~~~~~~~~~~~") def __call__(self, *args, **kwargs): statr = datetime.datetime.now() ret = self.fn(*args,**kwargs) datel = (datetime.datetime.now()-statr).total_seconds() print(datel) return ret @Point def add(x,y): """12312""" time.sleep(2) return x+y print(add(10,10))上面的类可以用在上下文管理,也可以用作字符串
1.增强功能 在代码执行的前后增加代码,以增强其功能.类似装饰器的功能 2.资源管理 打开了资源需要关闭,例如文件对象 网络连接 数据库连接等 3.权限验证 在执行代码之前,做权限的验证,在__enter__中处理
contextlib.contextmanager 它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__和__exit__方法 对下面的函数有要求:必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值 也就是说这个装饰器接收一个生成器对象作为参数
import contextlib @contextlib.contextmanager def foo(): print('abc') try: yield finally: print('123') with foo() as f: raise Exception() print(f)f接收yield语句的返回值 增加异常 增加try finally 当yield发生处为生成器函数增加了上下文管理.这是为函数增加上下文机制的方式
把yield之前的当做__enter__方法执行把yield之后的当做__exit__方法执行把yield的值为__enter__的返回值 总结 如果业务简单可以使用contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__和__exit__方法方便