Python上下文管理器的高级使用

在文件处理和网络编程时,对于打开的文件不管最后内容处理是否符合预期都要在结束时关闭文件。这时常见的处理方法是try catch finally 的方法

f = open("demo.txt", "a")

try:
    f = open("demo.txt", "a")
    raise Exception("读取内容报错")
except Exception as e:
    print(e)
finally:
    if f is not None:
    f.close()

使用错误捕获的方法有效的避免了文件打开没有关闭的情况。实现同样的功能有一种更加优雅的方法,那就是with关键字。

with 语法介绍:
上下文管理器(context manager)是 Python2.5 开始支持的一种语法,用于规定某个对象的使用范围。一旦进入或者离开该使用范围,会有特殊操作被调用。它的语法形式是 with…as…,主要应用场景资源的创建和释放。例如,文件就支持上下文管理器,可以确保完成文件读写后关闭文件句柄。

简单示例如下:

with open("demo.txt", "a+") as f:
    print("文件读取")

使用with就可以不用写冗长的try catch finally等处理流程,而且文件一定是安全打开和关闭。with之所以能做到是因为open对象中有打开文件的方法__enter__和关闭文件的方法__exit__,在执行到print之前with调用了__enter__方法,执行完print之后with调用了__exit__方法。并且不管打开文件之后是否会出错,with最终都会调用__eixt__方法。所以文件一定能安全关闭。

with 使用

一个对象想要支持with这种优雅的方法来管理,需要实现__enter____exit__两个方法。根据with是否有返回,可以分成两个类型。

  1. 没有返回值,如with open("demo.txt");
  2. 有返回值,如with open("demo.txt") as f;

没有返回值

class ContextDemo:
    def __init__(self):
        print("__init__")
        return 

    def __enter__(self):
        print("__enter__")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")

if __name__ == '__main__':
    with ContextDemo():
        print("我就是with中的操作")
__init__
__enter__
我就是with中的操作
__exit__

有返回值

with 的返回值实际上就是__enter__中return的结果。有return就有返回值,没有return就没有返回值

class ContextDemo:
    def __init__(self):
        print("__init__")
        return 

    def __enter__(self):
        print("__enter__")
        return 100

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")


if __name__ == '__main__':
    with ContextDemo() as num :
        print(f"我就是with中的操作,输出{num}")
__init__
__enter__
我就是with中的操作,输出100
__exit__

contextlib

contextlib是一个上下文管理器工具,工具中有一个contextmanager,可以将一个函数变成一个装饰器,用来支持with关键字。
通俗来说就是如果一个函数想要支持with就需要有__enter____exit__这两个方法。而contextmanager就可以将函数变成这样一个函数。

依然根据with是否有返回值,区分两中使用方法

不带返回值

from contextlib import contextmanager


@contextmanager
def manager_fun():
    print("enter")
    yield  
    print("exit")


with manager_fun():
    print("我就是with中的操作")
enter
我就是with中的操作
exit

函数中通过yield将函数分成三个部分:yield之前代码、yield之后代码、yield;
执行的流程也是三个步骤:

  1. with manager_fun with 进入时执行yield之前的代码部分 print("enter")
  2. 遇到yield中断,然后执行print("我就是with中的操作")
  3. 最后返回到yield后面,执行print("exit")

传统上下文管理器需要实现的__enter____exit__在contextlib中对应的就是:yield之前代码就是__enter__,yield代码之后就是__exit__

带返回值

from contextlib import contextmanager


@contextmanager
def manager_fun():
    print("enter")
    yield  100
    print("exit")


with manager_fun() as num:
    print(f"我就是with中的操作{num}")
enter
我就是with中的操作100
exit

with 可以有返回值,如果想实现返回值,在传统上下文管理器中是__enter__函数的返回值就是with的返回值,在contextlib中就是yield的返回值。
yield 返回一个值,然后程序中断暂停在这里。返回的值就是with的返回值。(听起来有点绕口)

contextlib 使用场景

有些场景下想要使用with来管理对象,但是目标对象并不支持,这时就可以通过contextlib来包装一个上下文管理器,达到管理资源的目的

posted @ 2022-06-01 21:49  金色旭光  阅读(243)  评论(0编辑  收藏  举报