类的上下文管理其实就是两个魔术方法 __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


评论