编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

更加优雅的操作系统资源与处理异常,深入理解Python上下文管理器

wxchong 2024-07-23 21:39:19 开源技术 32 ℃ 0 评论

说起上下文管理器,也许会比较陌生,要是说起下面这个例子,相信大家都非常熟悉,这种打开文件的方式就是使用了上下文管理器。

什么是上下文管理器

语法

说明

  • context_expr:是支持上下文管理协议的对象,也就是上下文管理器对象,负责维护上下文环境。
  • as var:是一个可选部分,通过变量方式保存上下文管理器对象。
  • with_suite:需要放在上下文环境中执行的代码块。

为什么要用上下文管理器?

使代码更加优雅,避免了琐碎操作及必要操作步骤的遗忘一般应用在如下两种场景中:

  • 更加优雅的操作资源,如文件、数据库操作。
  • 更加优雅的处理异常。

下文中也会就这两种场景一一举例说明。


自定义上下文管理器

想实现一个上下文管理,首先需要了解什么是上下文管理协议(Context Manager Protocol,上下文管理协议包括两个方法:

  • __enter__():定义上下文管理器在with语句创建的块的开头应该做什么。注意,_enter__的返回值绑定到with语句的目标,或者as后面的名称。
  • __exit__(exc_type, exc_val, exc_tb) :定义上下文管理器在其块被执行(或终止)后应该做什么。它可以用来处理异常、执行清理,或者执行总是在块中的操作之后立即执行的操作。如果块执行成功,exc_typeexc_valexc_tb将为None。否则,您可以选择处理异常或让用户处理异常;如果您想处理它,请确保在所有操作完成之后,_exit__返回True。如果不想让上下文管理器处理异常,_exit__返回False,则会抛出异常。

也就是说,当我们需要创建一个上下文管理器类型的时候,就需要实现__enter__和__exit__方法,这对方法就称为上下文管理协议,定义了一种运行时上下文环境。

接下来,动手写一个自定义的上下文管理器,示例如下:

上述代码执行结果如下:

通过日志的打印顺序,不难其执行过程,在实际应用中,可以将资源的连接操作放到__enter__中,将资源的关闭操作写在__exit__ 中。


更加优雅的操作资源

在上文中,提到上下文管理器常应用的两个场景,接下来我们看一下第一个场景,更加优雅的操作资源,我们在mysql、sqlite等数据库操作时,经常涉及链接数据库、执行相关操作、关闭链接等操作,让我们上下文管理器是如何优雅的实现数据库链接、执行、关闭操作的,如下:

如上,我们使用上下文管理器优雅的实现数据库的查询,执行上述代码,输出结果如下:


更加优雅的处理异常

介绍完了如何更优雅的操作文件/数据库资源,接下来,看一下上下文管理器常应用到的第二个场景,如何更优雅的处理异常,如下:

以上面的代码为例,“run parse {1}'.format(self.flag)” 将抛出IndexError 异常,接下来我们执行如下代码:

执行上述代码,输出结果如下:

居然没有抛出异常信息,程序运行完成,使用 with 将异常的处理隐藏起来,简化 try/finally 模式,使得代码的可读性更高。这就是上下文管理协议的一个强大之处,异常在__exit__ 中捕获并由我们决定是抛出还是在这__exit__ 中解决。

在__exit__ 里返回 True(默认为返回False,即使代码中没有return),相当于告诉Python解释器,这个异常已经捕获了,不需要往外抛了。若__exit__ 里返回 False,则抛出所触发的异常。如下

执行上述代码,输出结果如下:

我们发现当__exit__ 里返回 False,同时仍然执行了“run close connection ”操作,然后程序抛出所触发的异常,完美。


通过上面几个例子,可以大致总结出with 语句的执行流程:

  • 执行context_expr 以获取上下文管理器对象。
  • 调用上下文管理器的__enter__方法。
  • 若存在 as var 从句,则将 __enter__方法的返回值赋给 var。
  • 执行代码块 with_suite。
  • 调用上下文管理器的 __exit__方法,如果 with_suite 产生异常,那么该异常的 type、value 和 traceback 会作为参数传给 __exit__,否则传三个None。
  • 如果 with_suite 触发异常,且__exit__的返回值等于 False,则抛出该异常。
  • 如果 with_suite 触发异常,且 __exit__的返回值等于 True,则忽略该异常。

contextlib 模块

上面自定义上下文管理器,如果应用与一些简单的功能,难免有些繁琐,对于上下文的管理,Python也提供了内建的模块contextlib来实现相同的机制,这种通过生成器和装饰器实现的上下文管理器,看起来比with语句和手动实现上下文管理协议更优雅。

在被装饰的open_file函数里,必须是一个带有yield的生成器,在yield之前的代码,就相当于__enter__方法中的内容,在yield 之后的代码,就相当于__exit__ 方法中的内容。

执行上述代码,运行结果如下:

如果要处理异常,可以改成下面这个样子。

执行上述代码,运行结果如下:


千里之行始于足下,若感兴趣就动手操作一下吧,感谢您的转发、关注支持。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表