436 字
2 分钟
上下文管理器
2024-12-29

在 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)
  1. 执行 test() 方法,先打印出 before
  2. 执行 yield 'hello'test 方法返回,hello 返回值会赋值给 with 语句块的 t 变量
  3. 执行 with 语句块内的逻辑,打印出 t 的值 hello
  4. 又回到 test 方法中,执行 yield 后面的逻辑,打印出 after 不过有一点需要我们注意:在使用 contextmanager 装饰器时,如果被装饰的方法内发生了异常,那么我们需要在自己的方法中进行异常处理,否则将不会执行 yield 之后的逻辑。