043.Python异常处理_自定义上下文管理器

无奋斗不青春

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈

分隔线

自定义上下文管理器

    • 自定义上下文管理器
    • contextlib模块
      • @contextlib.contextmanager装饰器
      • contextlib.closing函数
      • contextlib.nested函数(Python3.x 已弃用)

自定义上下文管理器

  • 实际上就是创建一个类,实现__enter__()(进入)方法和__exit__()(退出)方法
  • __enter__()(进入)方法中执行一些准备操作
  • __exit__()(退出)方法会接收到语句体执行时抛出的异常类型、异常的值和追踪信息
  • 示例
    # 自定义上下文管理器
    class Test:def __enter__(self):print('enter方法')return 'enter方法的返回值'def __exit__(self, exc_type, exc_val, exc_tb):"""exc_type:异常类型exc_val:异常的值exc_tb:追踪信息对象Traceback"""print('exit方法', exc_type, exc_val, exc_tb)# 使用自定义上下文管理器
    with Test() as x:print(x)1 / 0# ============输出结果============
    # enter方法# enter方法的返回值# exit方法 <class 'ZeroDivisionError'> division by zero <traceback object at 0x000002009555F400># Traceback (most recent call last):
    #   File "E:\StudyCode\Python\14-异常处理\03-自定义上下文管理器.py", line 12, in <module>
    #     1 / 0
    # ZeroDivisionError: division by zero
    
  • 示例解析
    # with 后面的 Test() 是创建一个类实例对象-上下文管理器
    # 自动执行上下文管理器对象内部的 __enter__() 方法# 比如文件操作的 open(),就可以把文件打开操作放到 __enter__() 方法内部
    # 执行完 __enter__() 方法,会把这个方法的返回值赋值给 with 语句中 as 后面的变量 x
    # 执行语句体: print(x) 和 1 / 0
    # 最后自动执行上下文管理器对象内部的 __exit__() 方法,这个方法会接收到语句体抛出的异常(包含:异常类型、异常值和追踪信息对象)
    # __exit__() 方法的返回值为 True 则不会再将接收到的异常往外抛出,如果返回值为 False 则将接收到的异常继续往外抛出
    # 所有最后的清理操作都可以写到 __exit__() 方法中(比如文件操作中最后的关闭文件操作)
    
  • __exit__()方法中接收到的追踪信息对象,我们也可以获取到对象的相关信息
    # 自定义上下文管理器
    class Test:def __enter__(self):# print('enter方法')return 'enter方法的返回值'def __exit__(self, exc_type, exc_val, exc_tb):"""exc_type:异常类型exc_val:异常的值exc_tb:追踪信息对象"""# print('exit方法', exc_type, exc_val, exc_tb)# 获取追踪信息对象的内容import tracebackprint(traceback.extract_tb(exc_tb))return True# 使用自定义上下文管理器
    with Test() as x:# print(x)1 / 0# ========== 输出结果 ==========
    # [<FrameSummary file E:\StudyCode\Python\14-异常处理\03-自定义上下文管理器.py, line 22 in <module>>]
    

contextlib模块

@contextlib.contextmanager装饰器

  • 作用
    • 使用装饰器,让生成器变成上下文管理器
  • 知识回顾
    • 生成器:包含 yield 语句的函数,这个函数的执行结果就是“生成器”
    • 关于生成器
    def Test():print('Text函数', 1)yield '返回值1'print('Text函数', 2)
    
  • 使用装饰器,让生成器变成上下文管理器
    import contextlib@contextlib.contextmanager
    def Test():print('Text函数', 1)yield '返回值1'print('Text函数', 2)with Test() as x:2 / 1print(x)# ========== 输出结果 ==========
    # Text函数 1
    # 返回值1
    # Text函数 2
    
  • 执行分解
    • 使用@contextlib.contextmanager装饰之后
    • yield之前的语句就是上下文管理器中的__enter__方法
    • yield语句就是执行 语句体
    • yield之后的语句就是上下文管理器中的__exit__方法
  • 应用示例
    import contextlib@contextlib.contextmanager
    def Test():try:yieldexcept Exception as e:print('异常:', e)with Test() as x:2 / 0# ========== 输出结果 ==========
    异常: division by zero# 步骤解析
    # try语句写在yield之前,会先执行
    # 通过yield语句执行语句体 2/0,会抛出异常
    # 执行yield后面的except语句捕获异常
    

contextlib.closing函数

  • 作用
    • 使用函数,让拥有close()方法但不是上下文管理器的对象变成上下文管理器
    • 注意:这个对象中必须要有close()方法,而且是要求最后必须执行close()方法
  • 通过案例讲解
    • 有一个如下的类对象
      class Test:def tt(self):print('tt方法')def close(self):print('对象关闭,释放资源')
      
    • 使用需求:Test类实例对象每次调用完tt()方法后,都需要执行close()方法关闭对象,释放资源
    • 按照我们刚学的上下文管理器进行操作,就需要再Test类当中增加__enter__()__exit__()两个方法
      class Test:def tt(self):print('tt方法')def close(self):print('对象关闭,释放资源')def __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):self.close()# 要求:Test类实例对象每次调用完tt()方法后,都需要执行close()方法关闭对象,释放资源
      with Test() as x:x.tt()# ========== 输出结果 ==========
      # tt方法
      # 对象关闭,释放资源# 解析:为什么要在__enter__中返回一个self
      # 因为我们需要在with语句里面去执行实例方法tt(),所以我们需要拿到这个实例对象
      # 通过__enter__方法使用self将这个实例对象返回,就可以将这个实例对象赋值给with语句中的x
      # 再通过 x 去调用实例方法 tt()
      
    • 这样写的话,就每次都要添加__enter__()__exit__()两个方法,比较麻烦
    • 我们可以通过contextlib.closing()函数来解决这个问题
      class Test:def tt(self):print('tt方法')def close(self):print('对象关闭,释放资源')# 要求:Test类实例对象每次调用完tt()方法后,都需要执行close()方法关闭对象,释放资源import contextlibwith contextlib.closing(Test()) as x:x.tt()
      
    • 只需要在with语句中使用contextlib.closing()函数就能达到上面增加__enter__()__exit__()两个方法的效果

contextlib.nested函数(Python3.x 已弃用)

  • contextlib.nested()函数在Python3.x中已经已弃用,仅做了解即可!
  • 作用
    • 在Python2.7版本之前,完成多个上下文管理器的嵌套
  • 通过案例讲解
    • 需求:读取一个文件所有内容,再把内容写入到另一个文件中
    • 在Python2.7版本之前的写法
    with open('xx.jpg', mode='rb') as form_file:with open('xx2.jpg',mode = 'wb') as to_file:file_content = form_file.read()to_file.write(file_content)
    
    • 这样写的话,就需要用到多个open()对象的上下文管理器做嵌套
    • 在Python2.7之前,就可以用contextlib.nested()函数来处理
    import contextlibwith contextlib.nested(open('xx.jpg', mode='rb'), open('xx2.jpg', mode='wb')) as (form_file,to_file):file_content = form_file.read()to_file.write(file_content)
    
    • 但是,在Python3.x版本对这种多个上下文管理器的嵌套进行了优化,也不在需要用contextlib.nested函数了
    with open('xx.jpg', mode='rb') as form_file, open('xx2.jpg', mode='wb') as to_file:file_content = form_file.read()to_file.write(file_content)
    
    • 关于这个函数我们只需要了解一下就行,记住Python3.x版本的写法即可