with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。
比如文件使用后自动关闭、线程中锁的自动获取和释放等。
with open('test.txt', 'r') as f:
for line in f:
print(line)
运行机制
with VAR = EXPR:
BLOCK
等价于
VAR = EXPR
VAR.__enter__()
try:
BLOCK
finally:
VAR.__exit__()
VAR对应一个上下文管理器(context manager)。
上下文管理器
实现了__enter__()和__exit__()两个方法的类就是一个上下文管理器。
class MyContextManager:
def __enter__(self):
print('before block run')
def __exit__(self, exc_type, exc_val, exc_tb):
print('after block run')
使用上下文管理MyContextManager
with MyContextManager():
print('run block')
输出如下:
before block run
run block
after block run
MyContextManager也可以接受参数
class MyContextManager:
def __init__(self, name, age):
self.name = name
self.age = age
def __enter__(self):
print('before block run')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('after block run')
def show(self):
print('my name is:', self.name)
print('my age is:', self.age)
再次使用上下文管理MyContextManager
with MyContextManager('logan', 27) as myCM:
myCM.show()
my name is: logan
my age is: 27
这里用到了一个as: with context_manager as target
target对应context_manager的__enter__()方法的返回值,返回值可以是context_manager自身,也可以是其他对象。
contextlib.contextmanager
contextmanager可以对生成器函数进行装饰,返回结果是一个上下文管理器。
import contextlib
@contextlib.contextmanager
def MyGenerator():
print('before yield')
yield
print('after yield')
with MyGenerator():
print('run block')
before yield
after yield
这里yield nothing,下面给个yield something的例子
import contextlib
@contextlib.contextmanager
def MyGenerator():
print('before yield')
yield 'context manager'
print('after yield')
with MyGenerator() as cm:
print('run block')
print(cm)
context manager
两个注意点
1. yield之前的语句类似于__enter__(),yield之后的语句类似于__exit__()方法。
2. yield返回一个上下文管理器,如果使用as语句,会被赋值给as语句中的target。
try...finally
当在try范围内产生一个异常时,会立即跳转到finally语句块。当finally语句块执行完毕后,会继续向上一层引发异常。
参考资料: