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('我是你爹')
    
  • 相关阅读:
    MySQL JDBC驱动 01 Class.forName
    Sybase性能调试 Statistics
    MySQL InnoDB存储引擎 MySQL介绍
    Sybase性能调试 dbcc trace
    ASP.NET页面的生命周期
    注册JavaScript?
    泛型
    静态类和静态类成员
    构造函数
    MYSQL常用操作
  • 原文地址:https://www.cnblogs.com/KbMan/p/11267154.html
Copyright © 2011-2022 走看看