上下文管理器
上下文管理器提供了一种申请资源和释放资源的方法,并且能保证离开上下文管理器时,一定会执行释放资源的操作,从语言角度来讲仅可能避免了资源的泄露。
- “资源泄漏” 是一个统称,不仅包含了内存泄漏,还包含了文件描述符或句柄泄漏、数据库连接泄漏等,所以后续本文统一使用 “资源泄漏”。

在进入上下文管理器时,先申请资源,然后执行业务逻辑代码,不管在执行代码时是否抛出异常,在退出上下文管理器时,都会执行释放资源的操作。
上下文管理器类
当进入上下文管理器执行with语句时,python调用对象的__enter__方法,离开with时,调用对象的__exit__方法。
- __enter__方法一般时申请对象所需资源,然后返回一个对象,这个对象会被赋值给as之后的那个变量,返回的对象一般时self
- 类的构造方法和__enter__方法需要捕获所有可能的异常,否则__exit__方法不会被调用。在构造方法和__enter__函数中不能抛出任何异常,否则会造成资源泄露。
- 成功进入上下文管理器代码块时,在执行业务逻辑代码时,无论发生什么异常都会调用__exit__来释放资源
- 如果__exit__返回True, 那么将忽略__exit__中的任何异常将会正确退出上下文管理器
import time
class Context:
def __init__(self, n):
self._data = [i for i in range(n)]
def __enter__(self):
print("申请资源进入上下文管理器")
return self # 返回一个对象赋值给as后的变量
def __exit__(self, exc_type, exc_instance, traceback):
"""
exc_type: 异常类型
exc_instance: 异常实例
traceback: 回溯
如果没有异常,三个参数都为None
"""
print("释放资源推出上下文管理器")
@property
def data(self):
return self._data
if __name__ == "__main__":
with Context(10) as context:
print("执行业务逻辑代码")
for i in context.data:
print(i, end=' ')
print("\n业务逻辑代码执行结束")
__exit__方法
__exit__必须接收3个位置参数:
- 异常类型:exc_type
- 异常实例: exc_instance
- 回溯选择:traceback
如果__exit__接收一个异常,有三种选择:
- 向上传播异常,返回False
- 终止异常, 返回True
- 抛出不同的异常
import time
class Context:
def __init__(self, n):
self._data = [i for i in range(n)]
def __enter__(self):
print("申请资源进入上下文管理器")
return self # 返回一个对象赋值给as后的变量
def __exit__(self, exc_type, exc_instance, traceback):
"""
exc_type: 异常类型
exc_instance: 异常实例
traceback: 回溯
如果没有异常,三个参数都为None
"""
print("释放资源推出上下文管理器")
if exc_instance:
print("捕获%s异常"%exc_instance)
if exc_type == ZeroDivisionError:
print("除0异常, 选择中止异常")
return True
if exc_type == ValueError:
raise TypeError("碰到ValueError时抛出类型错误异常")
print("向上传播异常")
return False
@property
def data(self):
return self._data
if __name__ == "__main__":
with Context(10) as context:
# 除0不会报错,在__exit__方法中对于除0异常选择终止异常
# print(10/0)
# 数值错误异常会转为类型错误异常
# raise ValueError("抛出数值错误异常")
# 其他异常会选择向上传播异常
raise IndexError()
让函数支持上下文管理器
使用contextlib.contextmanager通过装饰器方式就可实现
from contextlib import contextmanager
@contextmanager
def context_func():
yield "context" # 使用yield返回一个对象赋值给as后的变量
if __name__ == "__main__":
with context_func() as var:
print(var)

浙公网安备 33010602011771号