上下文管理器
- 上下文管理器可以帮助我们自动分配和释放资源
- 上下文管理器需要配合with语句使用
比如进行文件操作的时候我们可能会忘记操作后关闭文件(file close),使用with open(filename, mode) as f
不需要我们手动关闭文件,不管处理文件中是否有异常出现,都能保证with语句执行完毕后关闭文件,有效防止资源泄露,安全多了。
# with 语句的一般格式
with context_expression [as target(s)]:
with-body
在执行with-body会调用上下文管理器的enter方法,执行完with-body之后再调用上下文管理器的exit方法
基与类的上下文管理器
- 基与类的上下文管理器需要我们实现对象的
__enter()__
和__exit()__
方法 - 我们需要在
__enter()__
中管理资源对象,在__exit__()
中释放资源 - enter 方法在 with 语句体执行前调用,with 语句将该方法的返回值赋给 as 字句中的变量,如果有 as 字句的话
# 模拟 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中处理这些异常。例如:
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、__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 中释放资源
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')