# 上下文管理器

URL: https://caijiao.org/python/ch10/context-managers
Source: docs/python/ch10/context-managers.md
Description: 本章介绍 Python 中的上下文管理器，讲解其定义、使用方式以及在资源管理中的应用。

上下文管理器是 [Python](/python/) 中用于资源管理的一种机制，它能够自动地管理资源的获取与释放，避免资源泄漏。在文件操作、数据库连接、网络套接字等场景中，上下文管理器提供了一种清晰且高效的方式来处理资源。

## `with` 语句与上下文管理器

在 Python 中，`with` 语句与上下文管理器一起使用，自动处理资源的清理工作。上下文管理器通常通过定义 `__enter__()` 和 `__exit__()` 方法来实现。

- `__enter__()`：在进入上下文时执行，通常用于资源的初始化或获取。
- `__exit__()`：在退出上下文时执行，通常用于清理资源（如关闭文件、释放连接等）。

### 使用 `with` 语句

`with` 语句用于简化资源的管理，它会在代码块执行完毕后自动调用上下文管理器的 `__exit__()` 方法，无论代码块是否发生异常，保证资源被正确释放。

```python
with open('example.txt', 'r') as file:
    content = file.read()
    print(content)
```

在上面的示例中，`open()` 返回一个文件对象，它实现了上下文管理器协议。`with` 语句确保文件在操作完成后自动关闭。

## 自定义上下文管理器

我们可以通过定义一个类并实现 `__enter__()` 和 `__exit__()` 方法来创建自定义上下文管理器。

### 示例：自定义上下文管理器

```python
class MyContextManager:
    def __enter__(self):
        print("资源已获取")
        return self  # 可以返回资源对象，供 with 块使用

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("资源已释放")
        # 如果有异常发生，返回 True 可以抑制异常，返回 False 则抛出异常
        if exc_type:
            print(f"发生异常: {exc_val}")
        return True  # 阻止异常传播

# 使用自定义上下文管理器
with MyContextManager() as manager:
    print("正在使用资源")
    # 这里可以抛出异常，查看资源管理器的行为
    # raise ValueError("发生错误")
```

输出：

```
资源已获取
正在使用资源
资源已释放
```

如果取消注释 `raise ValueError("发生错误")`，输出将包括：

```
资源已获取
正在使用资源
资源已释放
发生异常: 发生错误
```

在此例中，我们自定义了一个上下文管理器 `MyContextManager`，它在进入和退出上下文时执行自定义的逻辑。

## 上下文管理器与异常处理

`__exit__()` 方法还负责处理上下文中发生的异常。如果上下文中的代码块抛出异常，`__exit__()` 方法会接收到异常信息。可以通过 `exc_type`、`exc_val` 和 `exc_tb` 参数访问异常的类型、值和回溯信息。

### 捕获异常

在 `__exit__()` 方法中，如果返回 `True`，则异常会被抑制，不会继续传播；如果返回 `False`，则异常会重新抛出。

```python
class MyContextManager:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print(f"捕获到异常: {exc_val}")
        return True  # 阻止异常传播

with MyContextManager():
    print("执行代码块")
    raise ValueError("测试异常")
```

输出：

```
执行代码块
捕获到异常: 测试异常
```

在这个示例中，异常被 `__exit__()` 捕获并抑制，因此程序不会中断。

## `contextlib` 模块

Python 标准库中的 `contextlib` 模块提供了用于简化上下文管理器创建的工具。通过 `contextlib.contextmanager` 装饰器，我们可以将一个普通的生成器函数转换为上下文管理器，而无需定义类和 `__enter__`、`__exit__` 方法。

### 示例：使用 `contextlib.contextmanager`

```python
from contextlib import contextmanager

@contextmanager
def my_context_manager():
    print("资源已获取")
    yield  # 这里相当于 __enter__
    print("资源已释放")  # 这里相当于 __exit__

# 使用生成器作为上下文管理器
with my_context_manager():
    print("正在使用资源")
```

输出：

```
资源已获取
正在使用资源
资源已释放
```

在这个示例中，我们使用 `@contextmanager` 装饰器简化了上下文管理器的实现，避免了编写类和 `__enter__`、`__exit__` 方法。

## 总结

- 上下文管理器是用于管理资源的工具，确保资源的自动获取和释放，避免资源泄漏。
- `with` 语句与上下文管理器一起使用，可以在进入和退出上下文时执行特定的操作。
- 我们可以通过定义类并实现 `__enter__` 和 `__exit__` 方法来创建自定义上下文管理器。
- `contextlib.contextmanager` 装饰器提供了一种简便的方式来创建上下文管理器，使用生成器代替传统的类实现。

上下文管理器使得 Python 程序更加简洁、安全，特别是在处理文件、网络连接、数据库操作等资源密集型任务时，极大地提升了代码的可读性和健壮性。
