抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

上下文管理器

  • 上下文管理器可以帮助我们自动分配和释放资源
  • 上下文管理器需要配合with语句使用

比如进行文件操作的时候我们可能会忘记操作后关闭文件(file close),使用with open(filename, mode) as f不需要我们手动关闭文件,不管处理文件中是否有异常出现,都能保证with语句执行完毕后关闭文件,有效防止资源泄露,安全多了。

1
2
3
# with 语句的一般格式
with context_expression [as target(s)]:
with-body

在执行with-body会调用上下文管理器的__enter__方法,执行完with-body之后再调用上下文管理器的__exit__方法

基与类的上下文管理器

  • 基与类的上下文管理器需要我们实现对象的__enter()____exit()__方法
  • 我们需要在__enter()__中管理资源对象,在__exit__()中释放资源
  • enter 方法在 with 语句体执行前调用,with 语句将该方法的返回值赋给 as 字句中的变量,如果有 as 字句的话
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 模拟 Python 的打开文件、关闭文件操作
class Filemanager:
def __init__(self, name, mode):
print('calling __init__ method')
self.name = name
self.mode = mode
self.file = None

def __enter__(self):
print('caling __enter__ method')
self.file = open(self.name, self.mode)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):
print('caling __exit__ method')
if self.file:
self.file.close


# Filemanager为上下文管理器
# with Filemanager('test.txt', 'w') as f 是上下文表达式,f为资源对象
with Filemanager('test.txt', 'w') as f:
print('ready to write to file')
f.write('Hello World')

运行结果

  • 运行结果解析:
    • 1、with 语句调用__init__方法,初始化对象
    • 2、with 语句先暂存了Filemanager的__exit__方法
    • 3、然后调用__enter__方法,输出caling enter method,返回资源对象(这里是文件句柄)给f
    • 4、输出ready to write to file,将Hello World写入文件
    • 5、最后调用__exit__方法,输出caling exit method,关闭之前打开的文件流

注意:__exit__方法中的参数exc_type、exc_val、exc_tb分别表示exception type、exception value、traceback。进行资源回收时如果有异常抛出,那么异常的信息就会包含再这三个变量中,让我们可以再__exit__中处理这些异常。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Foo:
def __init__(self):
print('__init__ called')

def __enter__(self):
print('__enter__ called')
return self

def __exit__(self, exc_type, exc_value, exc_tb):
print('__exit__ called')
if exc_type:
print(f'exc_type: {exc_type}')
print(f'exc_value: {exc_value}')
print(f'exc_traceback: {exc_tb}')
print('exception handled')
return True

with Foo() as obj:
raise Exception('exception raised').with_traceback(None)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
输出结果:
---------------------------------------
1、__exit__返回 True
__init__ called
__enter__ called
__exit__ called
exc_type: <class 'Exception'>
exc_value: exception raised
exc_traceback: <traceback object at 0x00000234AA532F08>
exception handled
---------------------------------------
2、__exit__返回 False
__init__ called
__enter__ called
__exit__ called
exc_type: <class 'Exception'>
exc_value: exception raised
exc_traceback: <traceback object at 0x00000120D0324F88>
exception handled
Traceback (most recent call last):
File "e:\Blog\shansan\source\_posts\context.py", line 19, in <module>
raise Exception('exception raised').with_traceback(None)
Exception: exception raised
---------------------------------------

发生异常时,__exit__方法返回 True 表示不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理。

基与生成器的上下文管理器

  • 基于生成器的上下文管理器的实现需要使用@contextmanage装饰器
  • 我们需要在finally block 中释放资源
1
2
3
4
5
6
7
8
9
10
11
12
13
from contextlib import contextmanager

@contextmanager
def file_manager(name, mode):
try:
f = open(name, mode)
yield f
finally:
f.close()

with file_manager('test.txt', 'w') as f:
f.write('hello world')

参考

评论