1. 什么是上下文管理器?

上下文管理器(Context Manager)是Python中用于精确分配和释放资源的机制。它通过__enter__()__exit__()两个魔术方法实现了上下文管理协议,确保即使在代码执行出错的情况下,资源也能被正确清理。

# 经典文件操作对比
# 传统方式
f = open("data.txt")
try:
content = f.read()
finally:
f.close()

# 上下文管理器方式
with open("data.txt") as f:
content = f.read()

典型应用场景包括:

  • 文件操作(自动关闭)
  • 数据库连接(自动归还连接池)
  • 线程锁(自动释放)
  • 临时修改配置(自动恢复)

2. with语句的魔法

with语句是上下文管理器的语法载体,其工作原理如下:

class FileHandler:
def __enter__(self):
"""进入上下文时调用,返回资源对象"""
print("打开文件")
return self

def __exit__(self, *args):
"""退出上下文时调用,处理清理和异常"""
print("关闭文件")

with FileHandler() as f:
print("写入文件")
```

等价于 →
1. context = FileHandler()
2. f = context.__enter__()
3. 执行代码块
4. context.__exit__(异常信息)

输出顺序 →
打开文件
写入文件
关闭文件


## 3. 创建上下文管理器的两种方式
### 3.1 基于类的实现
```python
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None

def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 返回True表示已处理异常
return False

# 使用示例
with FileManager("test.txt", "w") as f:
f.write("Hello Context Manager!")

3.2 使用contextlib模块

对于简单的场景,可以使用生成器+装饰器的简洁写法:

from contextlib import contextmanager

@contextmanager
def simple_context():
print("进入上下文") # 相当于 __enter__
try:
yield "返回资源" # 此处为 as 后的变量
finally:
print("退出上下文") # 相当于 __exit__

# 使用
with simple_context() as value:
print(f"获取到: {value}") # 输出:获取到: 返回资源

执行流程

  1. 执行 yield 之前的代码(资源分配)
  2. 将 yield 的值传递给 as 后的变量
  3. 执行代码块
  4. 执行 yield 之后的代码(资源释放)

4. 异常处理

__exit__方法的三个参数专门处理异常:

exc_type: 异常类型
exc_value: 异常值
traceback: 调用栈信息

返回 True 表示已处理异常,阻止传播
返回 False 或 None 则允许异常向上抛出
正确处理异常:在__exit__中根据异常类型决定处理逻辑

class SafeExecutor:
def __exit__(self, exc_type, exc_val, traceback):
if exc_type is None:
print("正常退出")
return False

if issubclass(exc_type, (IOError, ValueError)): #当发生的异常类型是 IOError 或 ValueError 或其子类时,执行特定处理逻辑。
print(f"已处理预期异常: {exc_val}")
return True # 阻止异常传播

print(f"未处理异常: {exc_val}")
return False # 继续传播

# 使用示例
with SafeExecutor():
choice = random.choice([0,1])
if choice:
raise ValueError("测试值错误")
else:
raise Exception("未预期异常")