436 字
2 分钟
上下文管理器
在 python 开发的过程中,我们通常会用到 with,而 with 的背后就是上下文管理器
With 语法
f = open('1.txt')
for line in f:
...
f.close()
# 上面的代码会导致文件读取期间出现异常的话文件句柄无法释放
# 对于这种情况,我们可以使用with来解决
with open('1.txt') as f:
for line in f:
...
上下文管理器
那么 with 后面的代码是可以随意地吗?
with context_expression [as target(s)]:
with-body
答案是否定的,with 后的代码需要实现上下文管理器协议
在 python 中,只要实现了刷新方法就实现了上下文管理器协议
__enter__
: 在进入 with 前调用,会赋值给 with 的 target__exit__
: 退出 with 调用,一般作为异常处理
```python
class TestContext:
def __enter__(self):
print('__enter__')
return 1
def __exit__(self, exc_type, exc_value, exc_tb):
print('exc_type: %s' % exc_type)
print('exc_value: %s' % exc_value)
print('exc_tb: %s' % exc_tb)
with TestContext() as t:
print('t: %s' % t)
Contextlib 模块
对于需要上下文管理的场景,除了自己实现 enter 和 exit,另一种方法就是使用 contextlib 模块
使用了 contextlib,就可以把上下文当做一个装饰器来使用
from contextlib import contextmanager
@contextmanager
def test():
print('before')
yield 'hello'
print('after')
with test() as t:
print(t)
- 执行
test()
方法,先打印出before
- 执行
yield 'hello'
,test
方法返回,hello
返回值会赋值给with
语句块的t
变量 - 执行
with
语句块内的逻辑,打印出t
的值hello
- 又回到
test
方法中,执行yield
后面的逻辑,打印出after
不过有一点需要我们注意:在使用contextmanager
装饰器时,如果被装饰的方法内发生了异常,那么我们需要在自己的方法中进行异常处理,否则将不会执行yield
之后的逻辑。