Python-with及上下文管理器——__enter__()和__exit__()

上下文管理器与装饰器类似,它们都是包装其他代码的工具。但装饰器用于包装定义的代码块(如函数或类),而上下文管理器可以包装任意格式的代码块。
有一些任务,可能事先需要设置,事后做清理工作。这种时候用上下文管理器就可以相当方便地进行处理。
例如,在对文件进行读写操作时,通常得这样进行

try:
    file = open("/tmp/foo.txt")
    data = file.read()
finally:
    file.close()

这样的写法虽然没有问题,但总是需要手动关闭文件,很可能忘记关闭文件句柄,引起漏洞。

在python2.5后新增了关键字with,使用with语句即可进入上下文管理器,它可以在执行完代码之后自动帮我们关闭文件,因此上述代码可改写成:

with open('test.txt') as f:
    data = f.read()

with究竟是如何工作的,或者说上面提到的上下文管理器究竟是什么?
with语句的作用实际上就是对其后的代码求值(本例中即为调用open函数)。该表达式返回一个对象,该对象包含两个特殊方法:__enter__()__exit__()

  • 紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。

我们可以通过如下实例来看

class Sample:
    def __enter__(self):
        print("__enter__")
        return "sample"

    def __exit__(self, type, value, trace):
        print("__exit__")


def get_sample():
    return Sample()


with get_sample() as sample:
    print(sample)
>>>
__enter__
sample
__exit__

整个过程有如下几步:

  1. __enter__()方法被执行
  2. __enter__()方法返回的值赋值给as后的变量sample
  3. 执行代码块
  4. __exit__()方法被调用
    可以看出,with语句是进入上下文管理器的入口且默认执行__enter__函数,而退出上下文管理器时默认执行__exit__函数。

由上我们可以知道,打开和关闭资源(如文件和数据库连接)是编写上下文管理器的一个重要应用。但除此之外,上下文管理器还有另一个重要功能:

异常处理

with语句的表达式的作用是返回一个遵循特定协议的对象,该对象必须定义一个 __enter__方法和一个__exit__方法。

除了self参数,__enter__方法不接受任何参数,如果有as变量(as子句是可选项 ),返回值赋给as后的变量。

除了self参数,__exit__方法还带有三个位置参数:

  • exc_type:一个异常类型
  • exc_instance :一个异常实例
  • traceback :一个回溯

无异常时它们全为None,但如果在代码块内有异常发生,则参数被填充

上下文管理器必须定义__exit__方法,该方法可以选择性地处理包装代码块中出现的异常,或者处理其他需要关闭上下文管理器状态的事情。如果exit方法接收一个异常,它可以

  • 返回False实现异常的传播
  • 返回True终止异常
  • 抛出一个不同的异常,它将替代异常被发送出去

你可能感兴趣的:(Python-with及上下文管理器——__enter__()和__exit__())