Python的上下文管理器

2020-10-22  本文已影响0人  时间煮菜

什么是上下文管理器

上下文管理器是一个对象,它定义了在执行 with 语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用 with 语句(在 with 语句中描述),但是也可以通过直接调用它们的方法来使用。

首先我们看下面操作文件的代码,理清几个概念,不要弄混了

with open("test.txt") as f:
    print(f.readlines())

上下文管理协议

class Resource():
    def __enter__(self):
        print('===connect to resource===')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('===close resource connection===')

    def operate(self):
        print('===in operation===')


with Resource() as res:
    res.operate()

"""
输出

===connect to resource===
===in operation===
===close resource connection===
"""

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

  1. 使用上下文管理器会让代码看起来更简洁优雅,这也是Python一直追求的。我们可以用上下文管理器操作(创建/获取/释放)资源,如文件操作、数据库连接
  2. 也可以用上下文管理器处理异常。我们一般用try...except...来处理异常但是这样做一个不好的地方是,在代码的主逻辑里,会有大量的异常处理代理,这会很大的影响我们的可读性。如果用上下文管理器,就可以使用with将异常的处理隐藏起来。也就是说,with大大简化了try...except..语句的异常处理

举个栗子,下面的代码我们将操作1/0 这个错误。看看是否不报错。

class Resource():
    def __enter__(self):
        print('===connect to resource===')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('===close resource connection===')
        print(exc_type, exc_val, exc_tb)
        return True

    def operate(self):
        1/0  # 分母不能为0,这里应该报错


with Resource() as res:
    res.operate()

"""
输出

===connect to resource===
===close resource connection===
<class 'ZeroDivisionError'> division by zero <traceback object at 0x0000024A3C452E08>
"""

这就是上下文管理协议的一个强大之处,异常可以在__exit__ 进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在__exit__ 里返回 True(没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。

在 写__exit__ 函数时,需要注意的事,它必须要有这三个参数:

当主逻辑代码没有报异常时,这三个参数将都为None。

理解并使用装饰器 contextlib

上面说了,如果要定义上下文管理器,就需要在类中定义__enter____exit__。在Python中也提供了一个@contextlib装饰器,可以省略两个魔法方法。该装饰器位于contextlib模块下

from contextlib import contextmanager
from contextlib import contextmanager

@contextmanager
def open_func(file_name):
    # __enter__ 方法
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')

    # 【重点】:yield 返回的内容复制给as之后的变量
    yield file_handler

    # __exit__方法
    print('close file:', file_name, 'in __exit__')
    file_handler.close()
    return

with open_func('E:/hello.txt') as f:
    for line in f:
        print(line)
        
"""
输出

open file: E:/hello.txt in __enter__
1

2

3

close file: E:/hello.txt in __exit__
"""

上面代码的执行过程:

上下文管理器的三个好处

  1. 提高代码的复用率
  2. 提高代码的优雅度
  3. 提高代码的可读性
上一篇下一篇

猜你喜欢

热点阅读