十八. 上下文管理协议 __enter__ 和 __exit__

十八. 上下文管理协议 __enter__ 和 __exit__

1.什么是上下文管理协议

上下文管理协议就是 with 语句, 为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__()__exit__()方法

  • with 对象,触发对象的__enter__的执行
  • with同一级别写代码, 脱离了with,就会执行 __exit__
class Open:
    def __enter__(self):
        print("----->enter执行了")
        return "enter的返回值"

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("----->exit执行了")

with Open() as f:  # 触发 __enter__
    print(f)
print("---->结束")  # 触发 __exit__
'''
----->enter执行了
enter的返回值
----->exit执行了
----->结束
'''

2.exit 的三个参数

  • exc_type :异常类型
  • exc_val :异常信息
  • exc_tb :追溯信息

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

class Open:
    def __enter__(self):
        print("----->enter执行了")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("异常类型--->",exc_type)
        print("异常信息--->",exc_val)
        print("追溯信息--->",exc_tb)
        print("----->exit执行了")

with Open() as f:
    print(f)
    raise AttributeError("这里设置一个异常,则下面代码不会再运行")

print("---->结束")  # 这一行无法运行

'''
----->enter执行了
<__main__.Open object at 0x0000022E31429D08>
异常类型---> <class 'AttributeError'>
异常信息---> 这里设置一个异常,则下面代码不会再运行
追溯信息---> <traceback object at 0x0000022E3142F0C8>
----->exit执行了
(抛出异常) : AttributeError: 这里设置一个异常,则下面代码不会再运行
'''

3.为什么要使用上下文管理器

  • 可以自动的操作(创建/获取/释放)资源,如文件操作、数据库连接
  • 无需在使用 try...execept... 去处理异常

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

class Open:
    def __enter__(self):
        print("----->enter执行了")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("异常类型--->",exc_type)
        print("异常信息--->",exc_val)
        print("追溯信息--->",exc_tb)
        print("----->exit执行了")
        return True

with Open() as f:
    print(f)
    raise AttributeError("这里设置一个异常,但上面exit的return是True, 所以该异常被忽略")

print("---->结束")  # 这一行正常运行

'''
----->enter执行了
<__main__.Open object at 0x000001D6CC399D08>
异常类型---> <class 'AttributeError'>
异常信息---> 这里设置一个异常,则下面代码不会再运行
追溯信息---> <traceback object at 0x000001D6CC39F0C8>
----->exit执行了
---->结束
Process finished with exit code 0
'''

4.自定义一个 open ,可以进行文件操作

class Open:
    def __init__(self,path,mode="rt",encoding="utf-8"):
        # 拿到一个open对象,那么我们就可以借用open对象的方法,当然你也可以重写方法read(),write()等
        self.f = open(path,mode,encoding=encoding)  

    def __enter__(self):
        return self.f  # 返回open对象 (作用就是为了可以使用它的方法)

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:             # 如果不为空,说明with上下文内部出错误了
            print(exc_val)       # 打印一下错误信息
            print("错误已被忽略") # 提示一下不会结束程序
            return True          # return True 就是为了忽略错误
        self.f.close()           # 当with内部没有错误, 正常结束时进行资源清理工作(关闭文件等)

with Open("test.py","rt",encoding="utf-8") as f:
    res = f.read()
    print(res)

5.总结

使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关注这个问题

原文地址:https://www.cnblogs.com/songhaixing/p/14238441.html