介绍
尽管 Python 的上下文管理器(context managers)被广泛使用,但很少有人真正理解其背后的目的。这些语句通常用于文件的读写操作,它们通过确保特定资源仅在特定进程中使用,来帮助应用程序节约系统内存并改进资源管理。
在这个 Lab 中,你将学习如何使用 with 语句进行自动资源管理,然后你将使用两种不同的方法创建你自己的上下文管理器:一种是基于函数的(function-based)方法,另一种是基于类的(class-based)方法。
with 语句
在这一步,你将学习如何将 with 语句与上下文管理器(context managers),如文件对象一起使用。上下文管理器是一种对象,它会在一个上下文(一段代码块)开始和结束时收到通知。with 语句会自动处理这个通知过程。
例如,文件对象就是上下文管理器。当一个 with 代码块结束时,文件对象会自动关闭,即使代码块中发生了错误。这可以防止数据丢失和资源泄漏。
注意: 如果你不熟悉使用 WebIDE(类似 VS Code),请先在 这个实验 学习。
让我们实际看看它是如何工作的。在左侧的文件浏览器中,打开 main.py 文件。将以下代码复制并粘贴到其中。这段代码将首先使用 with 语句创建并写入一个文件,然后读取其内容。
## 我们将创建一个文件来演示 'with' 语句。
## 首先,让我们向文件中写入一些内容。
with open('my_file.txt', 'w') as f:
f.write('This is a test file.')
## 现在,让我们使用上下文管理器将其读回。
## 退出 'with' 代码块时,文件会自动关闭。
with open('my_file.txt') as f:
file_contents = f.read()
print(file_contents)
## 即使发生错误,文件也会在这里自动关闭。
print("\nFile has been read and closed.")
现在,从终端运行脚本以查看输出。
python3 main.py
你应该会看到以下输出,确认文件已成功写入和读取。with 语句确保在写入和读取操作之后,my_file.txt 都被正确关闭。
This is a test file.
File has been read and closed.
使用 with 语句是处理文件或网络连接等资源的推荐方式,因为它能使你的代码更简洁、更安全。
编写你自己的上下文管理器
在这一步,你将发现如何使用生成器函数(generator function)和 @contextlib.contextmanager 装饰器来创建自定义的上下文管理器。这是一种创建简单上下文管理器的简洁方法。
yield 语句之前的代码在进入 with 代码块时运行(设置逻辑),yield 语句之后的代码在退出时运行(清理逻辑)。通过在 with 语句中使用 as 关键字,yield 的值可以被赋给一个变量。
从文件浏览器中打开 function_cm.py 文件。将以下代码复制到文件中。
## 使用 contextlib 装饰器的基于函数的上下文管理器
import contextlib
@contextlib.contextmanager
def context_manager(num):
print('Enter') ## yield 之前的代码在 __enter__ 时运行
yield num + 1 ## yield 的值成为 'cm' 变量
print('Exit') ## yield 之后的代码在 __exit__ 时运行
with context_manager(2) as cm: ## cm 接收 yield 的值 (3)
print('Right in the middle with cm = {}'.format(cm))
现在,在终端中执行脚本。
python3 function_cm.py
你将看到以下输出。注意执行的顺序:yield 之前的代码首先运行,然后是 with 代码块内部的代码,最后是 yield 之后的代码。
Enter
Right in the middle with cm = 3
Exit
这种方法对于创建临时上下文非常有用,例如设置和拆除数据库连接或临时更改某个设置。
基于类的上下文管理器
在这一步,你将学习如何将上下文管理器实现为一个类。这种方法提供了更多的控制权,适用于更复杂的场景。要创建一个基于类的上下文管理器,你必须定义两个特殊方法:__enter__ 和 __exit__。
__enter__: 当进入with语句时调用此方法。它应该执行设置(setup)逻辑。如果提供了as关键字,它的返回值将被赋给as后面的变量。__exit__: 当with代码块退出时(无论是正常退出还是由于异常退出),都会调用此方法。它应该执行清理(cleanup)逻辑。
让我们创建一个。打开 class_cm.py 文件并将以下代码复制进去。
## 基于类的上下文管理器:实现 __enter__ 和 __exit__ 方法
class ContextManager:
def __enter__(self, *args, **kwargs): ## 进入 'with' 代码块时调用
print("--enter--")
return self ## 可以返回对象用作 'as' 变量
def __exit__(self, *args): ## 退出 'with' 代码块时调用
print("--exit--")
with ContextManager(): ## 调用 __enter__,完成后调用 __exit__
print("test")
从终端运行脚本。
python3 class_cm.py
输出展示了控制流。首先调用 __enter__ 方法,然后是 with 代码块内的代码,最后调用 __exit__ 方法进行清理。
--enter--
test
--exit--
基于类的方法非常强大,因为它允许你在一个可重用的对象中封装复杂的设置和拆卸(teardown)逻辑。
总结
恭喜你完成了本次实验(Lab)!你已经学会了如何使用上下文管理器在 Python 中有效地管理资源。
你从用于安全文件处理的常见 with 语句开始。然后,你学习了两种不同的方法来创建自己的自定义上下文管理器:
- 使用
@contextlib.contextmanager装饰器的基于函数的方法,它非常适合简单的设置和拆卸任务。 - 通过实现
__enter__和__exit__方法的基于类的方法,这非常适合更复杂的资源管理。
掌握这些技能对于编写能够正确处理资源的、干净、可靠且高效的 Python 代码至关重要。



