Python 的上下文管理器可以帮助我们控制和管理各种各样的系统资源(比如文件、锁和连接等),再加上 with 语法糖的使用,可以大大简化我们管理资源的代码成本,同时提高健壮性。
一些常用的场景如下:
with open('testfile', 'w') as f:
f.write('hello')
import threading
lock = threading.Lock()
with lock:
print('Lock')
with obj
语句的执行流程如下:
- 执行
obj.__enter__()
来说明当前正在进入一个新的上下文 obj.__enter__()
会返回一个对象用于with obj as ...
后的变量,当然也可以为空,这是可选的,通常情况下会返回self
自身- 执行 with 语句块中的语句
- 当离开上下文后,执行
obj.__exit__(type, value, traceback)
方法- 如果没有任何异常产生,那么三个参数都是
None
- 如果存在异常,那么就会设置对应的参数
- 如果没有任何异常产生,那么三个参数都是
obj.__exit__
方法会返回 True 或 False,分别表示被引发的异常是否得到了处理,如果为 False,那么异常会继续向上抛
下面是一个例子:
class ListTransaction(object):
def __init__(self, thelist):
self.thelist = thelist
def __enter__(self):
self.workingcopy = list(self.thelist)
return self.workingcopy
def __exit__(self, exctype, value, tb):
if exctype is None:
self.thelist[:] = self.workingcopy
return False
items = [1, 2, 3]
with ListTransaction(items) as working:
working.append(4)
working.append(5)
print(items)
try:
with ListTransaction(items) as working:
working.append(6)
working.append(7)
raise RuntimeError
except RuntimeError:
pass
print(items)
输出如下:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
可以看到,对已有列表的一系列修改只会在没有异常发生的时候才会生效,并使得原始列表变化。
另外标准库中提供了一个 contextmanager
的包装生成器函数,用来更加方便的自定义上下文管理器,比如下面:
from contextlib import contextmanager
@contextmanager
def ListTransaction(thelist):
workingcopy = list(thelist)
try:
yield workingcopy
thelist[:] = workingcopy
except RuntimeError:
print('nothing changed')
items = [1, 2, 3]
with ListTransaction(items) as working:
working.append(4)
working.append(5)
print(items)
with ListTransaction(items) as working:
working.append(6)
working.append(7)
raise RuntimeError
print(items)
输出如下:
[1, 2, 3, 4, 5]
nothing changed
[1, 2, 3, 4, 5]
yield
语句是一个分界线:
yield
之前的语句可以看做是__enter__()
方法- 传递给
yield
的值用作了__enter__()
方法的返回值 - 调用
__exit__()
方法时,执行将在yield
语句后恢复,如果发生了异常,yield
后面的语句将不会执行,当然你可以选择 try catch 住或者直接不管向上抛