python 进阶(16) -- 上下文管理器(with)


生我者何人,又岂由得我

什么是上下文管理器

我们经常在写 Python 代码的时候,会经常碰到下面的情况: - 当某条件为真时,执行某个代码块 - 当某条件为真时,循环执行某个代码块

另外,某些情况下,当我们需要执行上面所提到的某个代码块时,需要提前准备

某些,也可能需要在执行后结束某种状态

举个例子,比如写文件操作,也是下面需要使用到的,当我们执行写文件操作前

,必须得打开一个文件,写了完成之后,也必须要关闭这个文件

这样的一个过程,就称之为上下文管理,而实现这个过程的,就称之为上下文管

理器了


上下文管理协议

就像迭代器中有迭代协议(实现__iter__方法)一样,上下文管理器也有其对应的

协议,既上下文管理协议,其规定,上下文协议需要实现如下两个方法:

  • __enter__ 方法将在进入代码块前被调用

  • __exit__ 方法则在离开代码块之后被调用(即使在代码块中遇到了异常)

也就是对应上了上面所说到的执行前准备,执行后打扫的任务

另外,exit方法中有三个参数(exc_type, exc_val, exc_tb),分别表示: - exc_type: 错误的类型 - exc_val: 错误类型对应的值 - exc_tb: 代码中错误发生的位置

当然,参数的名字是可以更改的


实现上下文管理器

上下文管理器的实现由 with 语句生成,with + 后面的语句构成了上下文表达式

我们最常接触到的上下文管理器也就是上面所提到的例子,对文件的处理,如下:

with open(filename) as f:
    f.write(sometext)

翻译成比较通俗的就是下面的代码:

f = open(r'somefileName')
try:
    f.write(sometext)
finally:
    f.close()

诚如所见,with 语句的使用,大大精简了代码


with 原理

下面试着讲讲 with 关于文件方面的具体实现过程

先来看看一段代码:

class A:
    def __enter__(self):
        print('__enter__() is called')

    def __exit__(self, e_t, e_v, t_b):
        print('__exit__() is called')


with A() as a:
    print('got instance')

如果能够理解上下文协议的话,应该不难知道输出的结果

__enter__() is called
got instance
__exit__() is called

函数 nested

nested 可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句,跟 zip

() 函数有点相似

with nested(A(), B(), C()) as (X, Y, Z):
     # with-body

展开来:

with A() as X:
    with B() as Y:
        with C() as Z:
             # with-body

需要注意的是,发生异常后,如果某个上下文管理器的 exit() 方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常