Python进阶-day15: 上下文管理器(上下文容器)

liftword3周前 (05-06)技术文章9

学习目标

  1. 理解上下文管理器和 with 语句
  2. 掌握 with 语句的语法和用途,理解其在资源管理中的作用。
  3. 实现自定义上下文管理器
  4. 学会通过类(__enter__ 和 __exit__ 方法)和 contextlib 模块创建上下文管理器。
  5. 熟悉 contextlib 模块
  6. 使用 @contextmanager 装饰器简化上下文管理器实现。
  7. 处理异常和资源管理
  8. 理解上下文管理器如何确保资源正确释放,即使发生异常。
  9. 实践文件操作上下文管理器
  10. 实现一个自定义文件操作的上下文管理器,应用于实际场景。

概念解释

  1. 上下文管理器
  2. 上下文管理器是一个支持 __enter__ 和 __exit__ 方法的对象,用于管理资源(如文件、数据库连接、锁等)的分配和释放。
  3. 常见用途:确保资源在使用后正确清理,例如关闭文件或释放锁。
  4. with 语句
  5. Python 的 with 语句用于简化资源管理,自动调用上下文管理器的 __enter__ 和 __exit__ 方法。
  6. 语法:with 上下文管理器 as 变量: 代码块
  7. 优点:代码简洁,异常安全,自动清理资源。
  8. contextlib 模块
  9. Python 标准库中的模块,提供工具简化上下文管理器创建。
  10. @contextmanager 装饰器允许用生成器函数定义上下文管理器,减少样板代码。
  11. enter 和 exit 方法
  12. __enter__:在进入 with 语句时调用,返回供代码块使用的对象(如文件对象)。
  13. __exit__:在退出 with 语句时调用,处理清理工作(如关闭文件),可处理异常。
  14. 异常处理
  15. 上下文管理器的 __exit__ 方法接收异常信息(类型、值、追溯),可选择抑制异常(返回 True)或让异常抛出(返回 False)。

代码示例及用法

以下代码展示 with 语句、自定义上下文管理器(基于类和 contextlib)、以及异常处理的实现,附带详细注释。

python

# Day 15: 上下文管理器学习课程
# 主题:with 语句、contextlib 模块、自定义文件操作上下文管理器

# 1. 基础:使用 with 语句操作文件
def basic_with_statement():
    # with 语句自动管理文件资源,无需手动调用 close()
    with open('example.txt', 'w') as f:
        f.write('Hello, Context Manager!')
    print("文件已写入并自动关闭")

# 2. 自定义上下文管理器(基于类)
class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None
    
    def __enter__(self):
        # 进入 with 语句时调用,打开文件并返回文件对象
        print(f"打开文件: {self.filename}")
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 退出 with 语句时调用,关闭文件
        if self.file:
            self.file.close()
            print(f"关闭文件: {self.filename}")
        # 返回 False,不抑制异常(如有异常会抛出)
        return False

# 3. 使用 contextlib 模块简化上下文管理器
from contextlib import contextmanager

@contextmanager
def file_manager(filename, mode):
    # 使用 @contextmanager 装饰器,简化上下文管理器实现
    print(f"打开文件: {filename}")
    file = open(filename, mode)
    try:
        # yield 之前相当于 __enter__,返回文件对象
        yield file
    finally:
        # yield 之后相当于 __exit__,确保文件关闭
        file.close()
        print(f"关闭文件: {filename}")

# 4. 异常处理示例
def test_exception_handling():
    # 测试上下文管理器在异常情况下的行为
    try:
        with FileManager('test_error.txt', 'w') as f:
            f.write('尝试写入文件')
            # 模拟一个异常
            raise ValueError("模拟一个错误")
    except ValueError as e:
        print(f"捕获到异常: {e}")

# 5. 测试所有功能
def main():
    print("=== Day 15: 上下文管理器学习 ===")
    
    # 测试基础 with 语句
    print("\n1. 基础 with 语句示例:")
    basic_with_statement()
    
    # 测试基于类的上下文管理器
    print("\n2. 基于类的 FileManager 测试:")
    with FileManager('test_class.txt', 'w') as f:
        f.write('这是基于类的上下文管理器测试')
    
    # 测试基于 contextlib 的上下文管理器
    print("\n3. 基于 contextlib 的 file_manager 测试:")
    with file_manager('test_contextlib.txt', 'w') as f:
        f.write('这是基于 contextlib 的上下文管理器测试')
    
    # 测试异常处理
    print("\n4. 异常处理测试:")
    test_exception_handling()

# 主程序入口
if __name__ == "__main__":
    main()

运行结果(示例输出)

=== Day 15: 上下文管理器学习 ===

1. 基础 with 语句示例:
文件已写入并自动关闭

2. 基于类的 FileManager 测试:
打开文件: test_class.txt
关闭文件: test_class.txt

3. 基于 contextlib 的 file_manager 测试:
打开文件: test_contextlib.txt
关闭文件: test_contextlib.txt

4. 异常处理测试:
打开文件: test_error.txt
关闭文件: test_error.txt
捕获到异常: 模拟一个错误

代码解析

  1. 基础 with 语句
  2. 使用内置的 open() 函数作为上下文管理器,写入文件并自动关闭。
  3. 展示 with 语句的基本用法。
  4. 基于类的上下文管理器
  5. FileManager 类通过 __enter__ 打开文件,__exit__ 关闭文件。
  6. 返回文件对象供 with 语句使用,打印日志以观察执行流程。
  7. 基于 contextlib 的上下文管理器
  8. 使用 @contextmanager 装饰器,通过 yield 分隔进入和退出逻辑。
  9. 实现与 FileManager 相同的功能,但代码更简洁。
  10. 异常处理
  11. 模拟异常,验证上下文管理器在异常发生时仍能正确关闭文件。
  12. 使用 try-except 捕获异常,展示异常处理流程。
  13. 文件输出
  14. 生成 example.txt、test_class.txt、test_contextlib.txt 和 test_error.txt,分别存储对应内容。

使用场景说明

上下文管理器广泛应用于需要管理资源或状态的场景,以下是典型应用场景:

  1. 文件操作

场景:读写文件时确保文件正确关闭,避免资源泄漏。

示例:批量处理日志文件,写入数据后自动关闭。

代码中的 FileManager 和 file_manager 即为此场景设计。

  1. 数据库连接

场景:连接数据库执行查询后,自动关闭连接或回滚事务。

示例:使用上下文管理器封装数据库会话,确保提交或回滚事务。

类似代码:

@contextmanager
def database_connection():
    conn = connect_db()
    try:
        yield conn
    finally:
        conn.close()
  1. 线程锁和同步

场景:多线程编程中管理锁的获取和释放,避免死锁。

示例:保护共享资源(如计数器)访问。

示例代码:

from threading import Lock
lock = Lock()
with lock:
    # 访问共享资源
    counter += 1
  1. 计时和性能分析

场景:测量代码块的执行时间,用于性能优化。

示例:记录机器学习模型训练时间。

示例代码:

@contextmanager
def timer(description):
    start = time.time()
    yield
    elapsed = time.time() - start
    print(f"{description}: {elapsed:.2f} 秒")
  1. 临时状态管理

场景:临时更改环境变量或配置,完成后恢复。

示例:测试代码时临时修改日志级别。

示例代码:

@contextmanager
def temp_log_level(level):
    old_level = logger.level
    logger.setLevel(level)
    try:
        yield
    finally:
        logger.setLevel(old_level)
  1. 网络连接
  2. 场景:管理 socket 或 HTTP 连接,确保使用后正确关闭。
  3. 示例:爬虫程序中管理 HTTP 客户端连接。

总结

  • 学习成果:通过 with 语句、类实现、和 contextlib 模块,掌握了上下文管理器的核心机制,并实现了一个文件操作上下文管理器。
  • 关键点:上下文管理器简化资源管理,异常安全,适合文件、数据库、锁等场景。
  • 扩展练习
  • 为 FileManager 添加编码支持(如 encoding='utf-8')。
  • 实现一个计时上下文管理器,记录代码执行时间。
  • 尝试在 __exit__ 中处理特定异常(如 IOError)并抑制。

如果需要进一步讲解某个场景、扩展代码功能,或调试代码,请告诉我!

相关文章

python 锁Lock功能及多线程程序锁的使用和常见功能示例

锁(Lock)是Python中的一个同步原语,用于线程之间的互斥访问。它可以用来保护共享资源,确保在任意时刻只有一个线程可以访问共享资源,从而避免多线程并发访问引发的数据竞争和不一致性。下面分别详细说...

一文扫盲!Python 多线程的正确打开方式

一、多线程:程序世界的 "多面手"(一)啥是多线程?咱先打个比方,你去餐厅吃饭,一个服务员同时接待好几桌客人,每桌客人就是一个 "线程",服务员同时处理多桌事务就是 &...

24-2-Python多线程-线程操作(python多线程怎么用)

2-线程操作在Python程序中,可以通过“_thread”和“threading(推荐使用)”这两个模块来处理线程。在Python 3程序中,thread模块已废弃。可以使用 threading 模...

Python 如何通过 threading 模块实现多线程。

先熟悉下相关概念多线程是并发编程的一种方式,多线程在 CPU 密集型任务中无法充分利用多核性能,但在 I/O 操作(如文件读写、网络请求)等待期间,线程会释放 GIL,此时其他线程可以运行。GIL是P...

python线程之十:线程 threading 最终总结

小伙伴们,到今天 threading 模块彻底讲完。现在全面总结 threading 模块1、threading模块有自己的方法详细点击【threading模块的方法】threading 模块:较低级...

24-4-Python多线程-进程操作-案例

4-1-介绍4-1-1-Python的进程虽然Python语言支持创建多线程应用程序,但是Python解释器使用了内部的全局解释器锁定(GIL),在任意指定的时刻只允许执行单个线程,并且限制了Pyth...