python exit()没有定义_有趣的Python上下文管理器

b216c13b6132d508084b738169a65212.png

阅读耗时:9分钟

目录

  • 编写自定义上下文管理器

  • 从生成器到上下文管理器

  • 将上下文管理器编写为装饰器

  • 嵌套式上下文管理器

  • 组合式上下文管理器

  • 利用用上下文管理器创建SQLAlchemy session

  • 利用上下文管理器捕获异常

  • 利用上下文管理器处理网络请求

  • 备注

  • 资源

Python的上下文管理器非常适合调节资源的利用率和管理内存的开销。通常,它是以关键字with开头的语句:

621f3e50b33e92eaff1ad8f93e1a6d3f.png

在上面的例子中,当文件写入完成时,file.txt会自动关闭。这等价于:

6989b50438ab661590e6c8938de0926c.png

编写自定义上下文管理器

要编写自定义上下文管理器,需要创建一个包含__enter____exit__方法的类。让我们重新创建一个自定义的上下文管理器,它将执行与上述相同的工作流程。

b564f98c40dde3b436ddc736259a75f0.png

可以像常规上下文管理器一样使用上述类。

7c1580cb3b3e04afd18b221adde64080.png

从生成器到上下文管理器

上述通过使用__enter____exit__方法来编写类仅仅是创建上下文管理器的途径之一。另一个途径是通过导入contextlib.contextmanager库来实现。该库使用生成器创建上下文管理器。方法如下所示:

7611208f3ee90a2e1c6e495f226c3441.png

然后,与关键字with配合,实现其应有功能。

9607c063b0a2a7f86da674f03d68d2b9.png

它可以解释为:

f176d68d5f0b7e9fa0953e9bd8b4cfd3.png

所代表的设置类代码位于try...finally之前。注意关键字yield所在的位置,它所定义的值将赋值给as之后的变量,并保持值的稳定。如果存在一个未处理的异常,那么它将在生成器中yield位置被重新引发,然后执行finally代码块;反之,程序会优雅地运行下去,直至finally代码块运行结束。

让我们使用contextmanager装饰器实现CustomFileOpen的上下文管理器。

71fc69ff67c967c248530016b2771dbb.png

用法相同:

7ead182fef7c9f2ccb1f33251f50d117.png

将上下文管理器编写为装饰器

我们还可以将上下文管理器重写为装饰器。为此,在定义类时,须继承contextlib.ContextDecorator类。下面就编写一个名为RunTime的装饰器,该装饰器将能打开文件。并实现以下功能:

  • 打印向用户提供的功能说明

  • 打印运行该功能所需的时间

f3c09bfe6853ef5ec883489a7fcb46bf.png

使用方法:

db2e6e4d1c7196ec2e6aa18d1189c88b.png

返回方法:

50e33e120480a2336d8718c4d0b6c63c.png 867692f34ec6e585434dc4db0d5627c6.png

还可以通过contextlib.contextmanager创建功能相同的装饰器。

b6520bb0428a8abc5453f34531ea0700.png

嵌套上下文管理器

上下文管理器可以进行嵌套,如下所示:

362a03a4fe5751ebda2d9a04698f99f1.png edfc33434c87b803dad70f62c519cdfb.png

注意关闭的顺序。上下文管理器拥有类似堆栈的逻辑,应按相反的顺序退出。任何一个上下文管理器都可以处理异常,若此异常已经被某个管理器所处理,那么其它的管理器将不会收到有关此异常的任何信息;因此,如果发生异常,诸多嵌套的上下文管理器的逻辑顺序就成了运维的重要依据;而问题的另一方面,我们可以使用__exit__方法来引发异常,然后使用上下文管理器对其进行处理。

组合多个上下文管理器

我们可以组合多个上下文管理器。考虑这两个管理器。

b715a1e7a621fea1abd6031bbe0885b4.png

现在,使用装饰器语法将两者结合起来。以下函数采用上面的定义管理器ab并返回组合的上下文管理器ab

dc9eba420cd9c491d2f44aa4b5b71e59.png

可以用作:

5bef304c3e079677bf1cc2f0633f1a2f.png 2eafc565f201f82ac645c27e52a23677.png

利用contextlib.ExitStack库可以为诸多的上下文管理器提供组合支持。下面我们就使用ExitStack重写上下文管理器ab。此函数将各个上下文管理器及其参数作为元组,并返回组合的管理器。

fc2cebea9225edbac1f6d200ec5da29b.png dc71f0d900fc2ad80d1e6d2dd926177f.png 16fb5a7e3465da3d8946c6b66af3230c.png

如果要优雅地管理多个资源,也可以使用ExitStack。举个例子,假设需要根据目录中多个文件的内容来创建一个列表。那么让我们看看,如何通过强大的资源管理来避免意外的内存泄漏。

2c461e795bbc3925c411b26a9f6f8a86.png

使用上下文管理器创建SQLAlchemy session

SQLALchemy,是PythonSQL工具包和最有名的ORM架构,它使用session进行查询。 session将查询转换为事务并使其原子化。上下文管理器可以非常优雅编写事务型sessionSQLAlchemy中的基本查询工作流程如下所示:

11e25debc8ec0b4f7b3a6d6cf1620884.png

上面使用了上下文管理器创建了一个SQLite连接和一个session_scope session_scope负责自动处理异常情况下的提交和回滚。session_scope可通过以下方式进行查询:

b91da8b6b57982244e96bde51b7a13a1.png

使用上下文管理器对异常的抓取规则进行设置

这是我绝对喜欢的管理器功能。假设需要编写一个函数,希望它能避免具有类似日志功能的异常处理逻辑对核心逻辑的混淆, 那么可以编写一个装饰器类型的上下文管理器,来将普通处理逻辑与主逻辑解耦。接下来,我们写一个装饰器,它可以同时处理ZeroDivisionErrorTypeError

341204064203db68d366b21ca48b7861.png

现在,在发生异常的函数中使用它。fb5dac4a0fec67e7fa10dab966edcc64.png187dfda0c26cfc47afe004c359888843.pngdd2540fca74742023948b92270285469.png

看到了吧,errhandler装饰器正在完成繁重的工作。很整洁吧?

以下是使用上下文管理器将错误处理异常与主逻辑解耦的更复杂的示例。它还从main方法中隐藏了详细的日志记录逻辑。

fbe0c461469b8f48a523876fca9757e2.png

这将返回

202cb312d1fc7c1ed3f42b17fbb5e385.png

利用上下文管理器实现Http请求的持久化

上下文管理器的另一个很好的应用是使参数在多个http请求中保持不变。 Python的请求库有一个Session对象,如果您要向同一主机发出多个请求,那么底层的TCP连接将被重用,从而可以显着提高性能。以下示例直接取自requests的官方文档。我们在requests中保留一些cookie

9117d66c7426af867054149773ec908f.png

结果:

5d641e02c81be08c474d6a1ac61893fb.png

备注

所有代码段均已针对python 3.8更新。为避免冗余,我故意排除了带语句嵌套的示例,现在不建议使用contextlib.nested函数来创建嵌套的上下文管理器。

英文原文:https://rednafi.github.io/digressions/python/2020/03/26/python-contextmanager.html
译者:sky
fd8bfc92f6cc326639b961b5c78f1906.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值