类的上下文管理其实就是两个魔术方法 __enter__ 和 __exit__, 负责进入和退出。
# encoding = utf-8 __author__ = "mcabana.com" class A: def __init__(self): print('init ~~~~~~~~') def __enter__(self): # 在执行 with 语句块前,对要操作的实例的操作 return 'enter ~~~~~~~' def __exit__(self, exc_type, exc_val, exc_tb): # 在执行 with 语句块后,对要操作的实例的操作。执行结束时的 print('exit ~~~~~~~~~') return 'exit ~~~~~~~~~~~' with A() as f: print(f) print('with ~~~~~~~~~~')
__enter__ 负责资源的申请工作,__exit__负责资源的释放工作。有异常影响,__exit__的释放操作不受影响,如下:
就算是 system.exit结束进程,__exit__依然会进行资源释放。__exit__是由with进行释放的。
对于一些不方便进行资源释放的类,可以定义 enter 和 exit 方法,使用with进行资源释放。
关于with子句
class A: def __init__(self): print('init ~~~~~~~~') def __enter__(self): # 资源申请 return 100 def __exit__(self, exc_type, exc_val, exc_tb): # 资源释放 print('exit ~~~~~~~~~') a = A() with a as f: # f = a.__enter__() , f 就是 __enter__ 的返回值 print(a) print(f) print(a is f)
as 子句的对象 f, 其实就是前面实例的 __enter__方法的返回值,如果返回self,则 a is f
上下文的应用场景
- 增强功能
- 在代码执行的前后增加代码,以增强其功能。类似装饰器的功能
- 资源管理
- 打开了资源需要关闭,例如文件对象、网络连接、数据库连接等
- 权限验证
- 在执行代码之前,做权限的验证,在 __enter__ 中处理
上下文应用场景示例:
计算 add方法 的执行时间
# encoding = utf-8 __author__ = "www.mcabana.com" # 计算add函数的执行时间 import time import datetime def add(x, y): time.sleep(1) return x + y class Timeit: """ 计算函数的执行时间 """ def __init__(self, fn): # fn是传入的函数,只是一个“标识符” self._fn = fn 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('function <{}> took {}s'.format(self._fn.__name__, delta)) with Timeit(add) as t: print(t is add) # 因为 Timeit.__enter__() 返回的是传入的add, 所以t is add t(3, 4) # ==> add(3, 4)
contextlib.contextmanager
contextlib.contextmanager它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现__enter__ 和 __exit__ 方法。
对下面的函数有要求:必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值。
也就是这个装饰器接收一个生成器对象作为参数。
关于yield
函数中一旦出现yield,它就是一个生成器函数,返回生成器对象。
生成器函数中的return值,是生成器的最后一个元素,无法显示,所以生成器函数中一般没有return。
def foo(): # 函数中出现 yield 即为一个生成器函数,生成器函数返回一个生成器对象 yield 1 yield 100 for i in range(4): yield i + 100 return 10 f = foo() print(f) print(1, next(f)) # 1 print(2, next(f)) # 100 for i in range(4): print(i+3, next(f)) # 100, 101, 102, 103 print('last', next(f)) # 最后的reutrn值,是最有一个元素,无法显示
函数的上下文
https://www.hugbg.com/archives/2653.html
评论