共计 1943 个字符,预计需要花费 5 分钟才能阅读完成。
在前面的学习中,我们使用 with 语句来简化文件的打开与关闭操作。with 的核心是 上下文管理器 (Context Manager)。它能确保资源在使用后被正确释放,不论是否发生异常。今天我们将深入理解上下文管理器的原理,并学习如何自定义上下文管理器。
- 上下文管理器的原理
上下文管理器依赖两个特殊方法:
__enter__(self):在进入with语句时调用,通常用于资源的申请。__exit__(self, exc_type, exc_val, exc_tb):在离开with语句时调用,通常用于资源的释放。
举例说明:
class MyContext:
def __enter__(self):
print(" 进入上下文,资源已申请 ")
return " 资源对象 "
def __exit__(self, exc_type, exc_val, exc_tb):
print(" 退出上下文,资源已释放 ")
if exc_type:
print(f" 发生异常:{exc_val}")
return True # 阻止异常向外传播
# 使用上下文管理器
with MyContext() as res:
print(" 正在使用:", res)
# raise ValueError(" 模拟异常 ") # 可测试异常处理
运行结果:
进入上下文,资源已申请
正在使用:资源对象
退出上下文,资源已释放
- 文件处理中的上下文管理器
文件操作是最常见的应用场景:
with open("test.txt", "w") as f:
f.write("Hello, World!")
等价于:
f = open("test.txt", "w")
try:
f.write("Hello, World!")
finally:
f.close()
可以看到,with 自动帮我们调用了 __enter__ 和 __exit__,避免了忘记 close() 的问题。
- 自定义上下文管理器处理资源
例如,我们设计一个上下文管理器来模拟数据库连接:
class DatabaseConnection:
def __enter__(self):
print(" 建立数据库连接 ")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(" 关闭数据库连接 ")
if exc_type:
print(" 数据库操作出错:", exc_val)
return False # 让异常继续向外抛出
def query(self):
print(" 执行查询操作 ")
# 使用
with DatabaseConnection() as db:
db.query()
这样写可以确保数据库连接无论是否异常都会被正确关闭。
- 使用
contextlib简化自定义
Python 提供了contextlib模块,可以更简单地创建上下文管理器。
from contextlib import contextmanager
@contextmanager
def my_context():
print(" 进入上下文 ")
yield " 资源对象 "
print(" 退出上下文 ")
# 使用
with my_context() as res:
print(" 使用:", res)
这里 yield 前的代码相当于 __enter__,yield 后的代码相当于 __exit__。
- 上下文管理器的应用场景
- 文件操作(读写文件,自动关闭)
- 数据库连接(自动释放连接)
- 网络连接(如 Socket,使用后自动关闭)
- 线程锁(
threading.Lock用于多线程并发控制) - 临时环境配置(如切换目录后自动返回原目录)
- 结合线程锁的上下文管理器
import threading
lock = threading.Lock()
def safe_increment(counter):
with lock:
counter[0] += 1
counter = [0]
threads = []
for _ in range(100):
t = threading.Thread(target=safe_increment, args=(counter,))
threads.append(t)
t.start()
for t in threads:
t.join()
print(" 最终计数:", counter[0])
这里的 with lock 确保线程锁能被正确释放,避免死锁。
总结:
上下文管理器是 Python 资源管理的重要工具。通过实现 __enter__ 和 __exit__,或使用 contextlib,我们可以轻松实现安全的资源管理。掌握上下文管理器能让代码更优雅、更健壮,避免资源泄漏。
练习与思考:
- 自定义一个上下文管理器,进入时打印 " 开始任务 ",退出时打印 " 结束任务 "。
- 使用
contextlib.contextmanager编写一个上下文管理器,在进入时切换目录,退出时返回原目录。 - 设计一个上下文管理器,用于计时一段代码的执行时间。
正文完