Python基础入门 Day97 上下文管理器的原理与自定义

214次阅读
没有评论

共计 1943 个字符,预计需要花费 5 分钟才能阅读完成。

在前面的学习中,我们使用 with 语句来简化文件的打开与关闭操作。with 的核心是 上下文管理器 (Context Manager)。它能确保资源在使用后被正确释放,不论是否发生异常。今天我们将深入理解上下文管理器的原理,并学习如何自定义上下文管理器。

  1. 上下文管理器的原理
    上下文管理器依赖两个特殊方法:
  • __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(" 模拟异常 ")  # 可测试异常处理

运行结果:

 进入上下文,资源已申请
正在使用:资源对象
退出上下文,资源已释放
  1. 文件处理中的上下文管理器
    文件操作是最常见的应用场景:
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() 的问题。

  1. 自定义上下文管理器处理资源
    例如,我们设计一个上下文管理器来模拟数据库连接:
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()

这样写可以确保数据库连接无论是否异常都会被正确关闭。

  1. 使用 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__

  1. 上下文管理器的应用场景
  • 文件操作(读写文件,自动关闭)
  • 数据库连接(自动释放连接)
  • 网络连接(如 Socket,使用后自动关闭)
  • 线程锁(threading.Lock 用于多线程并发控制)
  • 临时环境配置(如切换目录后自动返回原目录)
  1. 结合线程锁的上下文管理器
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,我们可以轻松实现安全的资源管理。掌握上下文管理器能让代码更优雅、更健壮,避免资源泄漏。

练习与思考:

  1. 自定义一个上下文管理器,进入时打印 " 开始任务 ",退出时打印 " 结束任务 "。
  2. 使用 contextlib.contextmanager 编写一个上下文管理器,在进入时切换目录,退出时返回原目录。
  3. 设计一个上下文管理器,用于计时一段代码的执行时间。
正文完
 0
评论(没有评论)