Python中的上下文管理器

系统 126 0

Python中的上下文管理器,实际上就是实现了上下文管理协议的对象。在Python中打开文件的时候,我们需要确保文件被使用完毕之后,对其进行关闭操作——调用文件对象的close()方法。如果不使用上下文管理器,经典的处理方式就是将close()方法的调用放在一个finally语句中:

            
              f = open("www.log")

try:
    print("do something with file")
finally:
    f.close()
            
          

 这里finally的唯一作用就是确保文件对象最后被正确关闭,为此我们使用了一个try...finally语句块——这就需要我们在实现业务逻辑的时候,去考虑一些业务逻辑之外的东西,我们需要一个类似于自动内存回收(GC)的机制,来自动地帮我们做文件描述符的清理,让我们把精力都用在业务逻辑实现上。上下文管理器就可以达到这样的目的。而Python的文件对象是实现了这个协议了的:

            
              with open("www.log") as f:
    print("do something with file")
            
          

这和上面的try...finally风格的代码实现了等价的功能,只不过这里的代码更加简洁了,文件对象最后也可以被关闭。

上下文管理协议的定义非常简单,就是在被上下文包覆的代码执行前执行一个__enter__方法;在被包覆代码之后执行一个__exit__方法,根据这个定义我们就能写出一个非常简单的上下文管理器:

            
              class SimpleContext:
    def __enter__(self):
        print("before code block")

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


if __name__ == '__main__':
    with SimpleContext():
        print("I am code block")
            
          

运行这个代码的输出为:

            
              before code block
I am code block
after code block

            
          

可以看到,with块下的代码执行之前,运行了上下文协议的__enter__方法,with块下的代码执行之后,运行了上下文协议的__exit__ 方法。

可以通过在__enter__方法中返回一个值,将此值定义应用在with块中,可以通过as关键词来获取对这个值的引用,这就是上面看到的with open("www.log") as f中获取对f引用的方法:

            
              class SimpleContext:
    def __enter__(self):
        print("before code block")
        return 100

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


if __name__ == '__main__':
    with SimpleContext() as sc:
        print(sc)
        print("I am code block")
            
          

这样输出就变成了:

            
              before code block
100
I am code block
after code block
            
          

另外可以通过__exit__方法的回调参数exc_type, exc_val, exc_tb来访问with语句块的代码执行的异常信息,如果代码块一切正常,它们都是None;如果有异常抛出,就可以通过这三个参数来获取异常信息,做相应的处理。

以上就是上下文管理器的一个完整的内容了。但是Python中,为了让实现上下文管理器的过程更加简单,提供了另一种实现上下文协议的方法,就是通过contextlib模块中的contextmanager装饰器,结合使用生成器来实现:

            
              import contextlib

@contextlib.contextmanager
def SimpleContext():
    print("before code block")
    try:
        yield 100
    except Exception as e:
        print("handle e here, not in __exit__")
    finally:
        print("after code block")


if __name__ == '__main__':
    with SimpleContext() as sc:
        print(sc)
        print("I am code block")
            
          

这个实现的输出和上面的输出是一致的,只是写法的不同。第一种写法结构上更加清晰,第二种写法逻辑上更加连续,哪一种方法更好,作出自己的选择即可。

以上。


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请请扫描上面二维码支持博主1元、2元、5元等您想捐的金额吧,狠狠点击下面给点支持吧

发表我的评论
最新评论 总共0条评论