zoukankan      html  css  js  c++  java
  • 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('我是你爹')
    
  • 相关阅读:
    关闭编辑easyui datagrid table
    sql 保留两位小数+四舍五入
    easyui DataGrid 工具类之 util js
    easyui DataGrid 工具类之 后台生成列
    easyui DataGrid 工具类之 WorkbookUtil class
    easyui DataGrid 工具类之 TableUtil class
    easyui DataGrid 工具类之 Utils class
    easyui DataGrid 工具类之 列属性class
    oracle 卸载
    “云时代架构”经典文章阅读感想七
  • 原文地址:https://www.cnblogs.com/KbMan/p/11267154.html
Copyright © 2011-2022 走看看