浅谈上下文
本文最后更新于:2023年4月24日 凌晨
浅谈上下文
写 Python
也好久了,在编写代码时,通常会用到一个表达式,with .... as .....
,其中用的最多的可能是打开文件的时候使用这个表达式,但是却没想过为什么要这样写,或者这样写有什么方便之处,今天就来复习一下。
一、上下文管理器
何为上下文,在自然语言中,给你一段话,如果没有在上文和下文的情况下,你无法判断这段话在讲什么,也看不懂;这在计算机里也是如此,当一个程序独立存在的时候,它不依赖任何外部的数据或变量,此时它就不存在上下文,但是当多个程序关联起来,互相引用各自的数据或变量时,那么每个程序都不能独自运行了,这个时候就需要一个上下文,来管理这些各自的外部数据和变量。
而在 Python
中,则有了一个上下文管理器的概念,是指实现了 __enter__()
方法和 __exit__()
方法的对象;同时上下文管理器的存在也是为了管理 with
语句。
二、with
表达式的使用
在 with
表达式出现之前,对于资源的管理通常使用三段式的方式来实现,如下所示:
1 |
|
当运行发生异常的时候,finally
块里的代码会确保资源被正常的关闭,以此来引发内存泄漏,或者是下面这种实现方式:
1 |
|
当资源被正确打开时,没发生异常的情况下,代码最终回到 else
块,而资源会被正确的关闭。由此可见这样的代码是写的非常长的,同时对资源的管理,异常的捕捉又不是十分方便,这个时候 with
语句就派上用场了。
使用with
语句,可以优雅的实现资源的关闭,如下代码所示。
1 |
|
在with
语句中使用 open
函数时,完全不必担心打开的文件会没关闭,因为离开with
语句块之后,此时打印f.closed
已经可以看到为True
,为何 open
函数能在 with
语句里面做到自动关闭文件的操作,因为它内部实现了 __enter__()
和 __exit__()
方法。
三、实现自定义上下文管理器
实现自定义上下文的前提是,要在自定义类里面实现 __enter__()
方法和 __exit__()
方法。
__enter__()
:当with
语句块运行后, 会在上下文管理器对象里执行__enter__()
方法,通常情况下,这个方法应该返回一个赋值给as
后变量的对象,默认情况下为None
,同时这个是可选的,如果不需要返回,那么同时也不需要使用as
语句。标准上来说应该返回self
。__exit__()
:当with
语句结束后,上下文管理器会调用__exit__()
方法,效果等同于finally
关键字。
1 |
|
当程序进入 with
语句块之后,__exit__()
方法会帮我们处理好异常,但是 __exit__()
语句需要接受 4 个参数,第一个是 self
,其他三个参数分别是异常类型,异常属性,异常跟踪信息。
四、使用装饰器实现上下文管理器
如果只是为了创建一个上下文管理器而创建一个类的话,未免也太麻烦了,这个时候 contextlib
的 contextmanager
装饰器就起到了作用,使用这个装饰器可以轻松的在函数上实现上下文管理器,同时它采用的是生成器的实现方式; 以下是代码示范。
1 |
|
五、异步上下文装饰器
前面的都是基于同步的上下文管理器,contextlib
里还提供了异步的上下文管理器,使用 asynccontextmanager
实现,同样的,这个方法需要放在异步的方法上,才会起作用。
1 |
|