with 语句的作用

with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。一般访问文件资源时我们会这样处理:

f = open('c:\test.txt', 'r')
data = f.read()
f.close()

这样写没有错,但是容易犯两个毛病:
(1)如果在读写时出现异常而忘了异常处理。
(2)忘了关闭文件句柄

以下的加强版本的写法:

f = open('c:\test.txt', 'r')
try:
    data = f.read()
finally:
    f.close()

以上的写法就可以避免因读取文件时异常的发生而没有关闭问题的处理了。代码长了一些。但使用 with 有更优雅的写法:

with open(r'c:\test.txt', 'r') as f:
    data = f.read()

with 的实现

class Test:
    def __enter__(self):
        print('__enter__() is call!')
        return self

    def dosomething(self):
        print('dosomethong!')

    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__() is call!')
        print(f'type:{exc_type}')
        print(f'value:{exc_value}')
        print(f'trace:{traceback}')
        print('__exit()__ is call!')

with Test() as sample:
      pass

当对象被实例化时,就会主动调用__enter__()方法,任务执行完成后就会调用__exit__()方法,
另外,注意到,__exit__()方法是带有三个参数的(exc_type, exc_value, traceback),
依据上面的官方说明:如果上下文运行时没有异常发生,那么三个参数都将置为 None,
这里三个参数由于没有发生异常,的确是置为了 None, 与预期一致.

修改后不出异常了

class Test:
    def __enter__(self):
        print('__enter__() is call!')
        return self

    def dosomething(self):
        x = 1/0
        print('dosomethong!')

    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__() is call!')
        print(f'type:{exc_type}')
        print(f'value:{exc_value}')
        print(f'trace:{traceback}')
        print('__exit()__ is call!')
        return True

with Test() as sample: