一、上下文管理协议with

在我们创建类时,为了让其实例兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

  • __enter__()会在with语句出现(实例化对象)后执行
  • __exit__()会在with语句的代码块实行完毕才会执行

    class Open:
      def __init__(self, name):
          print('执行__init__')
          self.name = name
    
      def __enter__(self):  # 在实例化打开文件时即触发,在with时触发
          print('执行__enter__')
          return self  # return的self会赋值给f,相当于通过Open类实例化处对象f
    
      def __exit__(self, exc_type, exc_val, exc_tb):  # 在with中的代码块执行完毕才会触发
          print('执行__exit__')
    
    
    with Open('a.txt') as f:  # 会触发enter  '执行__enter__',相当于--》f=Open('a.txt').__enter__()
      print(f)  # <__main__.Open object at 0x01477270>
      print(f.name)  # 'a.txt'
    print('*' * 10)  # 先---'执行__exit__' , 后---'*********'
    
    """
    Output:
    ---------------------------------------------
    执行__init__
    执行__enter__
    <__main__.Open object at 0x000000000210B208>
    a.txt
    执行__exit__
    **********
    ---------------------------------------------
    """

    二、__exit__的细节

    __exit__()中有三个参数分别代表异常类型,异常值和追溯信息,执行了__exit__则表示with语句执行完毕

    1、若__exit__返回值不为True,则:

  • a、若with语句中没有异常,则程序正常执行
  • b、若with语句中出现异常,则程序会执行到with中出错的语句并执行__exit__,然后程序终止,‘吐出’异常
class Open:
    def __init__(self, name):
        print('执行__init__')
        self.name = name

    def __enter__(self):
        print('执行__enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('执行__exit__')
        if exc_type is not None:
            print(exc_type)  # <class 'AttributeError'>
        if exc_val is not None:
            print(exc_val)  # 'Open' object has no attribute 'age'
        if exc_tb is not None:
            print(exc_tb)  # <traceback object at 0x0000000002583288>


with Open('a.txt') as f:
    print(f)
    # print(f.age)
    # 因为f对象没有age属性,则出现异常,程序执行到该句时将异常传递给__exit__的三个参数,并结束程序执行,报错
    print(f.name)  # 该行语句后面的语句都不会执行,包括with语句的以外的语句也不会执行
print('*' * 10)

"""
Output:
---------------------------------------------------------
执行__init__
执行__enter__
    print(f.age)
AttributeError: 'Open' object has no attribute 'age'
<__main__.Open object at 0x000000000257E4A8>
执行__exit__
<class 'AttributeError'>
'Open' object has no attribute 'age'
<traceback object at 0x0000000002583288>
---------------------------------------------------------
"""

2、若__exit__返回值为True,则:

  • a、若with语句中没有异常,则程序正常执行
  • b、若with语句中出现异常,则程序会执行到with中出错的语句并执行__exit__,‘吞掉’异常。然后with语句中剩下的语句不会执行,但是会继续执行with语句以外的语句
class Open:
    def __init__(self, name):
        print('执行__init__')
        self.name = name

    def __enter__(self):
        print('执行__enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('执行__exit__')
        if exc_type is not None:
            print(exc_type)  # <class 'AttributeError'>
        if exc_val is not None:
            print(exc_val)  # 'Open' object has no attribute 'age'
        if exc_tb is not None:
            print(exc_tb)  # <traceback object at 0x00000192DE9BB680>
        return True


with Open('a.txt') as f:
    print(f)
    print(f.age)  # 因为f对象没有age属性,则出现异常,程序执行到该句时将异常传递给__exit__的三个参数,并结束程序执行,'吞掉异常'不会报错
    print(f.name)  # 该行语句后面的with中的语句都不会执行,但是with语句的以外的语句会继续执行
print('*' * 10)  # '*********'

"""
Output:
---------------------------------------------------------
执行__init__
执行__enter__
<__main__.Open object at 0x00000192DD070430>
执行__exit__
<class 'AttributeError'>
'Open' object has no attribute 'age'
<traceback object at 0x00000192DE9BB680>
**********
---------------------------------------------------------
"""

三、作用及好处:

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

文章目录