Python上下文管理器详解:with语句管理成对操作,初级到高级用法全解析

目录

1. 前言

2. 最最基本的一个上下文管理器

3. 自定义上下文管理器基本用法

4. 上下文管理器的高级用法

4.1 通过生成器函数实现上下文管理( contextlib 模块)

4.2 处理异常

4.3 嵌套使用上下文管理器

5. 总结


1. 前言

在 Python 中,资源管理是一个至关重要的主题。无论是文件操作、数据库连接,还是网络请求等场景,我们都需要确保资源被正确地获取和释放,以避免资源泄漏、数据损坏等问题。而 Python 的上下文管理器,能够帮助我们精准地掌控这些成对的操作,让代码不仅更加简洁,而且更具健壮性和可维护性。

在阅读此文前需明白魔法方法概念以理解__enter__和__exit__,请阅读以下这篇博客:

《python中的对魔法方法的理解,__call__、__add__、__str__、__len__、__init__等等常见魔法方法分别什么意思?》

2. 最最基本的一个上下文管理器

上下文管理器是 Python 中用于定义执行代码块之前和之后所需逻辑的工具,它允许我们使用 with 语句来创建一个临时的上下文,并在该上下文范围内执行代码。

最典型的例子就是文件操作,而我将以此构造一个最最简单的上下文管理器。

普通文件操作:

f = open('test.txt')
try:
    pass
finally:
    f.close()

而运用上下文管理器的文件操作: 

with open('test.txt') as f:
    pass

代码一下便是简洁直观了,在这段代码中,open 函数返回一个文件对象,这个对象就是一个上下文管理器。

有点绕,但是初步只需要知道,当我们使用 with 语句时,它会在进入代码块之前自动调用文件对象的 __enter__ 方法打开文件,并在退出代码块时自动调用 __exit__ 方法关闭文件。这样,我们就不必担心忘记关闭文件而导致资源泄漏的问题了。

3. 自定义上下文管理器基本用法

一个类要成为上下文管理器,需要实现两个特殊方法:__enter____exit__

__enter__ 方法会在进入 with 代码块时被调用,它通常用于获取资源、初始化对象等操作,并返回一个在代码块中使用的对象。

__exit__ 方法则在退出 with 代码块时被调用,用于释放资源、清理环境等工作。__exit__ 方法还可以接收异常相关的参数,这使得它能够处理在代码块中可能发生的异常情况。

举个例子,我们来创建一个简单的上下文管理器,用于测量代码块的执行时间:

import time

class Timer:
    def __enter__(self):
        self.start_time = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        end_time = time.time()
        print(f"Code block executed in {end_time - self.start_time:.4f} seconds")

使用这个上下文管理器:

with Timer() as timer:
    # 模拟一些耗时操作
    time.sleep(2)

运行结果为:

在这个例子中,当进入 with 代码块时,Timer 类的 __enter__ 方法被调用,记录开始时间并返回 self。在代码块执行完毕后,__exit__ 方法被调用,计算并打印代码块的执行时间。

4. 上下文管理器的高级用法

4.1 通过生成器函数实现上下文管理( contextlib 模块)

除了手动定义类来实现上下文管理器,Python 的 contextlib 模块提供了一个装饰器 contextmanager,可以让我们更方便地创建上下文管理器。它通过生成器函数的方式,将代码分为进入上下文和退出上下文的两部分。

from contextlib import contextmanager

@contextmanager
def my_context_manager():
    print("Entering the context")
    yield
    print("Exiting the context")

with my_context_manager() as orange_con:
    print("Inside the context")

在这个例子中,my_context_manager 函数被 contextmanager 装饰。当进入 with 代码块时,函数中的代码会执行到 yield 语句之前的部分(即打印 "Entering the context"),然后将控制权交给代码块中的内容。当代码块执行完毕后,函数会从 yield 处继续执行,完成退出上下文的操作(即打印 "Exiting the context")。

4.2 处理异常

上下文管理器的 __exit__ 方法可以接收三个参数:exc_typeexc_valexc_tb,分别代表异常类型、异常值和异常 traceback 信息。通过检查这些参数,我们可以在 __exit__ 方法中决定是否处理异常,或者进行一些特定的清理操作。

class MyOpen:
    def __init__(self, filepath):
        print('Entering constructor of MyOpen')
        self.filepath = filepath

    def __enter__(self):
        print('Entering __enter__ of MyOpen')
        return self.filepath

    def __exit__(self, exc_type, exc_value, traceback):
        print('Entering the __exit__ of MyOpen')
        if exc_type is ValueError:
            print('Caught a ValueError')
        return True

with MyOpen('test.txt') as f:
    raise ValueError('A ValueError occured')
    print(f'The value of f is {f}')

运行结果为:

可以看到 ,最后的  print(f'The value of f is {f}')这个语句并没有执行,那是因为返回一个错误信息后,程序直接进入__exit__方法运行,上下文管理器就到此为止了。如果想要其被执行,可以将它放在 with 块之外,或者在 with 块中使用 try...except 捕获异常。例如:

with MyOpen('test.txt') as f:
    try:
        raise ValueError('A ValueError occured')
    except ValueError:
        print('Exception handled inside the with block')

print(f'The value of f is {f}')

这样,即使在 with 块中抛出异常并被捕获,print 语句仍然可以执行。 

在这个例子中,当 with 代码块中抛出异常时,__exit__ 方法会捕获到异常信息,并打印出来。由于 __exit__ 方法返回了 True,所以异常不会继续向外传播,程序不会崩溃。

4.3 嵌套使用上下文管理器

在实际开发中,我们经常会遇到需要同时管理多个资源的情况,比如同时打开多个文件、连接多个数据库等。上下文管理器支持嵌套使用,我们可以使用多个 with 语句,或者利用 contextlib.ExitStack 来简化嵌套管理。

with open("file1.txt", "w") as f1, open("file2.txt", "w") as f2:
    f1.write("Hello, File 1!")
    f2.write("Hello, File 2!")

或者使用 ExitStack

from contextlib import ExitStack

with ExitStack() as stack:
    file1 = stack.enter_context(open("file1.txt", "w"))
    file2 = stack.enter_context(open("file2.txt", "w"))
    file1.write("Hello, File 1!")
    file2.write("Hello, File 2!")

这两种方式都可以实现对多个资源的上下文管理,选择哪种方式取决于具体的代码风格和需求。

5. 总结

Python 的上下文管理器为我们提供了一种优雅、简洁且强大的方式来管理资源的获取和释放。通过实现 __enter____exit__ 方法,或者使用 contextlib 模块的工具,我们可以在代码块的前后自动执行特定的逻辑,确保资源被正确地管理,避免资源泄漏和异常未处理等问题。

在实际开发中,熟练掌握上下文管理器的使用,能够让我们编写出更高效、更可靠的 Python 程序。无论是处理文件、数据库连接,还是其他需要成对操作的场景,上下文管理器都值得我们深入理解和广泛应用,它是 Python 编程中不可或缺的一部分,也是提升代码质量和开发效率的利器。我是橙色小博,关注我,一起在人工智能领域学习进步。

作者:橙色小博

物联沃分享整理
物联沃-IOTWORD物联网 » Python上下文管理器详解:with语句管理成对操作,初级到高级用法全解析

发表回复