with 的语法格式:
with context_expression [as target(s)]:
pass
上下文管理器协议
使用 with 语法块,with 后面的的对象需要实现「上下文管理器协议」
在 Python 中,一个类实现以下两个魔法方法,就实现了「上下文管理器协议」:
__enter__:在进入with语法块之前调用,返回值会赋值给with的target__exit__:在退出with语法块时调用,一般用作异常处理
class Test:
def __enter__(self):
print('function: __enter__')
return 1
def __exit__(self, exc_type, exc_val, exc_tb):
print(f'exc_type: {exc_type}')
print(f'exc_val: {exc_val}')
print(f'exc_tb: {exc_tb}')
with Test() as t:
print(f't: {t}')
"""
function: __enter__
t: 1
exc_type: None
exc_val: None
exc_tb: None
"""
with Test() as t:
print(f't: {t}')
a = 1/0
"""
t: 1
exc_type: <class 'ZeroDivisionError'>
exc_val: division by zero
exc_tb: <traceback object at 0x0000021F57012B40>
"""
说明:__enter__方法在进入with后的函数块之前被调用,并将该函数返回值赋值给t,然后执行函数块中内容,执行过程中若发生异常信息,则交给__exit__方法处理
exc_type:异常类型exc_value:异常对象exc_tb:异常堆栈信息
此外,之所以 with 能够自动关闭文件资源,就是因为内置的文件对象实现了「上下文管理器协议」,这个文件对象的 __enter__ 方法返回了文件句柄,并且在 __exit__ 中实现了文件资源的关闭,另外,当 with 语法块内有异常发生时,会抛出异常给调用者。
contextlib 模块
Python 标准库提供的 contextlib 模块,可以把上下文管理器当成一个「装饰器」来使用,该模块提供了 contextmanager 装饰器和 closing 方法。
contextmanager 装饰器的使用
使用 contextmanager 装饰器和 yield配合,实现和前面上下文管理器相同的功能
from contextlib import contextmanager
@contextmanager
def test():
print('--before--')
yield 1
print('--after--')
with test() as t:
print(t)
"""
--before--
1
--after--
"""
说明:
- 执行
test()方法,先打印出before - 执行
yield 'hello',test方法返回,hello返回值会赋值给with语句块的t变量 - 执行
with语句块内的逻辑,打印出t的值hello - 又回到
test方法中,执行yield后面的逻辑,打印出after
使用 contextmanager 装饰器,不用再写一个类来实现上下文管理协议,只需要用一个方法装饰对应的方法即可。但是如果被装饰的方法内发生了异常,那么我们需要在自己的方法中进行异常处理,否则将不会执行 yield 之后的逻辑。
案例:
from contextlib import contextmanager
@contextmanager
def test(a):
print('before')
try:
if a == 0:
raise Exception('分母不能为0')
yield 1/a
except Exception as e:
print(e)
finally:
print('after')
with test(0) as t:
print(t)
closing方法
相当于实现了类似Cpp中析构函数的作用
from contextlib import closing
class Test(object):
# 定义close方法才可以使用closing装饰器
def close(self):
print('closed')
with closing(Test()) as t:
print('test')
"""
test
closed
"""