zoukankan      html  css  js  c++  java
  • Python

    什么是上下文管理器

    官方解释...

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

    一顿花里胡哨猛如虎,结果我也不太懂

    简单一句话

    同时包含 __enter__() 和 __exit__() 方法的对象就是上下文管理器

    __enter__(self)

    • 进入上下文管理器自动调用的方法
    • 该方法会在 with ... as ... 代码块执行之前执行
    • 如果 with 语句 as 子句,且该方法返回值,那么该方法的返回值会被赋值给 as 子句后的变量,最常见的 with open('file_path', 'w') as file: 
    • 该方法可以返回多个值,因此在 as 子句后面也可以指定多个变量(多个变量必须由“()”括起来组成元组)

    __exit__(self, exc_type, exc_value, exc_traceback)

    • 退出上下文管理器自动调用的方法,会返回一个布尔类型的值
    • 该方法会在 with ... as ... 代码块执行之后执行
    • 如果 with ... as ... 代码块成功执行结束,程序自动调用该方法,且三个参数都为 None
    • 如果 with ... as ... 代码块执行时发生异常,通过 sys.exc_info() 得到异常信息,三个参数值分别是:异常类型、异常信息、异常回溯信息类型

    有哪些常见上下文管理器?

    打开文件

    with open('file_path', 'w') as file:
        file.write('hello world !')

    拆分了解

    • 上下文表达式: with open('file_path', 'w') as file: 
    • 上下文管理器: open('file_path', 'w') 
    • file:可以理解为资源对象

    执行顺序

    1. 先执行 open() 的 __enter__() 方法,将返回值赋值给 file
    2. 执行 file.write('hello world !') 
    3. 最后执行 open() 的 __exit__() 方法

    自定义上下文管理器

    其实有两种方式

    1. 基于类实现
    2. 基于生成器实现

    基于类实现上下文管理器

    只需要给对象添加一个 __enter__ 和一个 __exit__ 方法

    import sys
    
    
    class Resource:
        def __init__(self, name):
            self.name = name
            print("== 初始化方法 ==")
    
        def __enter__(self):
            print(f"** 进入上下文管理器自动调用:name is {self.name}")
            # 可以返回任意类型的值
            return {"name": self.name}
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print(f"## 退出上下文管理器自动调用:", sys.exc_info(), exc_type, exc_val, exc_tb)
            if exc_tb is None:
                print("没有异常时关闭资源")
            else:
                print("遇到异常时关闭资源")

    通过 with 来调用该上下文管理器

    也称为:使用 with ... as ... 管理资源

    with Resource("小菠萝") as r:
        print(r)

    console 输出结果

    == 初始化方法 ==
    ** 进入上下文管理器自动调用:name is 小菠萝
    {'name': '小菠萝'}
    ## 退出上下文管理器自动调用: (None, None, None) None None None
    没有异常时关闭资源

    __exit__() 方法的三个参数值都是 None

    with 代码块抛出异常

    with Resource("异常小菠萝") as r:
        print('[with代码块] 异常之前的代码')
        raise Exception("抛出了 Exception")
        print('[with代码块] ~~~~~~~~异常之后的代码')

    console 输出结果

    == 初始化方法 ==
    ** 进入上下文管理器自动调用:name is 异常小菠萝
    [with代码块] 异常之前的代码
    ## 退出上下文管理器自动调用: (<class 'Exception'>, Exception('抛出了 Exception'), <traceback object at 0x10e203200>) <class 'Exception'> 抛出了 Exception <traceback object at 0x10e203200>
    遇到异常时关闭资源
    Traceback (most recent call last):
      File "/Users/polo/Documents/pylearn/第七章:文件相关/1_上下文管理器.py", line 36, in <module>
        raise Exception("抛出了 Exception")
    Exception: 抛出了 Exception 

    代码块抛出异常的时候,可以看到 __exit__() 方法的三个参数值的确来源于 sys.exc_info() 

    总结

    • 无论 with 代码块是否有异常,最终都会自动调用 __exit__() 方法
    • 当抛出异常时,__exit__() 默认返回 None,会重新抛出异常到外面,让 with ... as ... 以外的代码来处理异常
    • 反之,如果返回 True,就会忽略异常,不再对异常进行处理

    __exit__() 返回 True

        def __exit__(self, exc_type, exc_val, exc_tb):
            print(f"## 退出上下文管理器自动调用:", sys.exc_info(), exc_type, exc_val, exc_tb)
            if exc_tb is None:
                print("没有异常时关闭资源")
            else:
                print("遇到异常时关闭资源")
                return True
    
    
    # 再次运行
    with Resource("异常小菠萝") as r:
        print('[with代码块] 抛出异常之前的代码')
        raise Exception
        print('[with代码块] 抛出异常之后的代码')

    console 输出结果

    == 初始化方法 ==
    ** 进入上下文管理器自动调用:name is 异常小菠萝
    [with代码块] 异常之前的代码
    ## 退出上下文管理器自动调用: (<class 'Exception'>, Exception('抛出了 Exception'), <traceback object at 0x100e29200>) <class 'Exception'> 抛出了 Exception <traceback object at 0x100e29200>
    遇到异常时关闭资源

    不再抛出异常

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

    通过装饰器 contextlib.contextmanager,来定义自己所需的基于生成器的上下文管理器

    from contextlib import contextmanager
    
    
    @contextmanager
    def file_manager(name, mode):
        try:
            # 1、打开文件
            file = open(name, mode)
            # 2、返回文件资源对象
            yield file
        finally:
            # 3、关闭文件
            file.close()
    
    
    with file_manager('a.txt', 'w') as file:
        print(file)
        file.write('hello world')
    • 函数 file_manager() 就是一个生成器
    • 当执行 with as 语句时,获取文件资源对象,生成器暂停执行,返回文件资源对象并赋值给 file
    • 当 with 语句执行完后,生成器继续执行剩余代码,关闭文件,释放资源

    总结

    • 基于生成器的上下文管理器时,不再用定义 __enter__() 和 __exit__() 方法
    • 但需要加上装饰器 @contextmanager

    with 语句的教程

    https://www.cnblogs.com/poloyy/p/15335965.html

  • 相关阅读:
    计算机二进制总结
    java-集合排序,队列,散列表map以及如何遍历
    java-Collection,List简单使用与方法/(集合使用-中)
    java-Date类与集合(上)
    java-正则、object中的两个方法的使用
    java-注释、API之字符串(String)
    Java-面向对象三大特征、设计规则
    java-多态、内部类
    java-修饰词、抽象类、抽象方法
    java-重载、包修饰词以及堆栈管理
  • 原文地址:https://www.cnblogs.com/poloyy/p/15335954.html
Copyright © 2011-2022 走看看