什么是Python中的上下文管理器Context Managers

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

在Python中提供了一种用于管理资源的对象被称为是上下文管理器(Context Managers),通过上下文管理器可以保证代码在执行前后能够正确的分配和释放资源。通常与with 语句一起使用。

上下文管理器的作用是什么

在Python中使用上下文管理器的主要作用就是能够确保在代码块执行的前后资源都能够进行合理的分配和释放,例如在一些文件处理场景、数据库链接场景中,对资源进行管理。提供了一种在使用资源时确保资源的正确管理的方式,无论代码块是否正常执行都能保证资源的正确处理。

具体而言,上下文管理器有以下几个作用:

  • 资源管理上下文管理器可以用于管理各种类型的资源,例如文件资源、数据库连接、网络连接、线程锁等。
  • 异常处理上下文管理器可以处理代码块执行过程中可能出现的异常情况。可以捕获异常并执行相应的清理操作,这样可以保证资源能够得到正确释放,避免出现异常情况
  • 简化代码上下文管理器可以简化代码,通过使用with语句操作,将资源的获取和释放逻辑封装在一个上下文环境中,使代码更加简单易懂。
  • 线程安全上下文管理器可以提供线程安全的资源管理方案。保证了在多线程环境下能够正确地管理共享资源,避免出现竞态条件或死锁等问题。

也就是说,上下文管理器提供了一种比较优雅的资源管理方式,可以保证资源在使用的过程中被正确的分配和释放管理,这样可以保证在出现代码异常的时候保证资源操作能够得到正确的处理,从而提高代码的健壮性和可靠性。

如何实现上下文管理器?

上下文管理器可以通过如下的两种方式实现

使用类实现上下文管理器

可以定义一个类,并在这类中实现 __enter__()__exit__() 两个方法。其中__enter__() 方法在进入代码块前执行,用于获取资源或执行一些准备工作;而__exit__() 则是要方法在退出代码块后执行,用于释放资源或执行清理操作。如下所示

class MyContextManager:
    def __enter__(self):
        # 获取资源或执行准备工作
        print("Entering the context")
        return self  # 返回资源对象

    def __exit__(self, exc_type, exc_value, traceback):
        # 释放资源或执行清理操作
        print("Exiting the context")
        if exc_type is not None:
            print(f"An exception of type {exc_type} occurred")
            # 处理异常
        return False  # True表示忽略异常,False表示传播异常

with MyContextManager() as cm:
    # 在这个代码块中可以使用资源
    print("Inside the context")

使用装饰器实现上下文管理器

当然除了上面通过类的方式来实现上下文管理器,我们还可以通过定义一个函数装饰器 contextlib.contextmanager来实现,通过使用yield语句将代码分成两个部分:第一部分是进入代码块前的部分,也就是在yield语句之前执行,而退出代码块后的部分在 yield 语句之后执行。如下所示。

from contextlib import contextmanager

@contextmanager
def my_context_manager():
    print("Entering the context")
    # 获取资源或执行准备工作
    yield
    print("Exiting the context")
    # 释放资源或执行清理操作

with my_context_manager():
    # 在这个代码块中可以使用资源
    print("Inside the context")

但是无论是通过那种方式来实现,它们都需要使用with语句来创建一个上下文环境,并在其中安全地使用资源,确保资源的正确释放。

如何实现自定义的上下文管理器?

编写自定义的上下文管理器其实就是上下文管理器的类方式实现,创建一个类,并在其中实现 __enter__()__exit__() 方法。这两个方法是上下文管理器协议的一部分,用于在进入和退出代码块时执行相应的操作。如下所示。

class MyContextManager:
    def __enter__(self):
        # 在进入代码块前执行的操作,例如获取资源或执行准备工作
        print("Entering the context")
        return self  # 返回资源对象

    def __exit__(self, exc_type, exc_value, traceback):
        # 在退出代码块后执行的操作,例如释放资源或执行清理工作
        print("Exiting the context")
        if exc_type is not None:
            # 如果在代码块中发生异常,可以在这里进行处理
            print(f"An exception of type {exc_type} occurred")
            # 返回True表示忽略异常,返回False表示传播异常
        return False

# 使用自定义上下文管理器
with MyContextManager() as cm:
    # 在这个代码块中可以使用资源
    print("Inside the context")

在上面的这例子中,我们通过MyContextManager 类来实现了上下文管理器的协议。在with语句中我们可以使用这个上下文管理器,来做保证资源的正确管理资源使用。

而我们提到的with语句就是用来简化资源管理操作的,通过这个语句来保证在代码执行完成之后去自动的关闭或者释放资源,最常见的操作就是在文件操作、网络连接、数据库链接等场景中,需要通过close显式调用的关闭资源的场景。

相关文章

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

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

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

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

python 多线程程序加锁、解锁、锁应用场景示例

锁(Lock)是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 模块:较低级...