Python中的上下文管理器

操作文件对象时可以:

with open('a.txt') as f:
    '代码块'

上述叫做上下文管理协议,即with语句。

想象一下,你有两个需要结对执行的相关操作,然后,还要在他们中间放置一段代码。比如打开一个文件,操作文件,然后关闭该文件。

打开文件和关闭文件就是一个结对的操作。

上下文管理器的常见用例:是资源的加锁与解锁,文件的打开与关闭。

上下文管理器

上下文管理器协议:是指类需要实现 __ enter __ 和 __ exit __ 方法。

就跟迭代器有迭代器协议一样,迭代器协议需要实现 __ iter __ 和 __ next __ 方法。

上下文管理器,也就是支持上下文管理协议的对象,简单点讲就是,实现了 __ enter __ 和 __ exit __两个方法的类。这个类也叫做,上下文管理器的类。

写一个Open类,这个类是一个上下文管理器:

class Open:
    def __init__(self, filepath, encoding):
        self.filepath = filepath
        self.encoding = encoding

    def __enter__(self): # 当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量
        print('当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with 中代码块执行完就执行我这个函数')


with Open('1.txt', 'UTF-8') as f:
    print('with 里面的代码块')
'''
结果:
当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量
with 里面的代码块
with 中代码块执行完就执行我这个函数
''' 

__ exit __(self, exc_type, exc_val, exc_tb):

里面的三个参数分别代表:异常类型,异常值,追溯信息。

注意:with语句中的代码块出现异常后,with后的代码都无法执行

基于类的实现:完整实现Open方法

一个上下文管理器的类,起码要定义 __ enter __ 和 __ exit __ 方法。

class Open:
    def __init__(self, filepath, method):
        self.file = open(filepath, method, encoding='utf-8')

    def __enter__(self):
        return self.file

    def __exit__(self, type, value, traceback):
        self.file.close()


with Open('1.txt', 'w') as f:
    f.write('1111111111')

我们来看看底层发生了什么?

  1. with语句先暂存了 Open 类的 __ exit __ 方法
  2. 然后调用 Open 类的 __ enter __ 方法
  3. __ enter __ 方法打开文件并返回给with语句
  4. 打开的文件句柄传递给 as 后面的 f 参数
  5. 执行with里面的代码块。
  6. 调用之前暂存的 __ exit __ 方法
  7. 关闭文件

在第4步和第6步之间,如果发生异常,Python会将异常的type,value,traceback传递给 __ exit __ 方法。

当异常发生时,with语句会采取哪些步骤?

  1. with把异常的type, value, traceback 传递给 __ exit __ 方法
  2. with让 __ exit __ 处理异常
  3. 如果 __ exit __ 返回的是True, 那么这个异常就被优雅的处理了。
  4. 如果 __ exit __ 返回的是True以外的任何东西,那个这个异常将被with 语句抛出。

当 __ exit __()返回值为True, 那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行.。

完整模拟Open:

class Open:
    def __init__(self, filepath, mode='r', encoding='utf-8'):
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding

    def __enter__(self):
        self.file = open(self.filepath, mode=self.mode, encoding=self.encoding)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        self.file.close()
        return True


with Open('1.txt', 'w', encoding='utf-8') as f:
    f.write('哈哈哈')
    f.werwer # 抛出异常,交给exit处理。后面的代码正常运行

优点

  1. 使用with的语句的目的就是把代码块放入with中执行, with结束后,自动完成清理工作,无需干预。
  2. 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在 __ exit __ 中定制自动释放资源的机制。

基于生成器实现一个上下文管理器

contextlib模块:可以使用一个生成器实现一个上下文管理器,而不是使用一个类。众所周知,在类中还需要实现 __ enter __ 和 __ exit __ 。

from contextlib import contextmanager

@contextmanager
def point(x, y):
    print('在yield之前')

    yield x * y  # yield出去的值赋给 as 后面的变量

    print('在yield之后')


with point(3, 4) as p:
    print('p',p)
    
    
'''
结果:
在yield之前
p 12
在yield之后
'''

利用contextlib模块实现一个open

@contextmanager
def my_open(path):

    f = open(path, mode='w')

    yield f  # 把这个f 赋给as后面的变量

    f.close()

with my_open('2.txt') as f:

    f.write('我是你爹')
原文地址:https://www.cnblogs.com/KbMan/p/11267154.html