在实际的编码中有一些任务,事先要做一些设置,事后要做一些处理,这时with就能提供一个简洁优雅的处理。
一般访问文件操作:
1 f = open('../test.txt', 'r') 2 data = f.read() 3 f.close()
但是这样写有忘记写关闭文件访问,这样会导致内存不能回收的浪费,甚至导致其他危险性后果。
如果直接使用with语法则会自动处理关闭文件操作
1 with open('../test.txt', 'r') as f: 2 data = f.read()
with就是一个上下文管理器,处理进入和退出所需运行时上下文以执行代码块,后面返回的对象必须有 _enter_()/_exit_()这两个方法,pytho中官方定义说明如下(https://docs.python.org/2/reference/datamodel.html#context-managers):
object.__enter__(self)
进入与此对象相关的运行时上下文。with语句将将此方法的返回值绑定到语句的AS子句中指定的目标(如果有设置的话)
object.__exit__(self, exc_type, exc_value, traceback)
退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文运行时没有异常发生,那么三个参数都将置为None。
如果有异常发生,并且该方法希望抑制异常(即阻止它被传播),则它应该返回True。否则,异常将在退出该方法时正常处理。
请注意, __exit__()方法不应该重新抛出传入的异常,这是调用者的职责。
下面举例说明他的原理:
1. 无异常发生时的例子:
#!/user/bin/env python3 #-*- coding:utf-8 -*- 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: sample.dosomething() >> __enter__() is call! dosomethong! __exit__() is call! type:None value:None trace:None __exit()__ is call!
以上的实例Text,我们注意到他带有__enter__()/__exit__()这两个方法,当对象被实例化时,就会主动调用__enter__()方法,任务执行完成后就会调用__exit__()方法,另外,注意到,__exit__()方法是带有三个参数的(exc_type, exc_value, traceback), 依据上面的官方说明:如果上下文运行时没有异常发生,那么三个参数都将置为None, 这里三个参数由于没有发生异常,的确是置为了None, 与预期一致.
2. 有异常发生时,会抛出异常的例子:
以下例子在上面稍做了一些修改,让运行时产生异常,看看这三个参数的赋值情况:
#!/user/bin/env python3 #-*- coding:utf-8 -*- 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: sample.dosomething() >> __enter__() is call! Traceback (most recent call last): __exit__() is call! type:<class 'ZeroDivisionError'> File "C:/Users/xxx/PycharmProjects/Test1/test.py", line 23, in <module> value:division by zero sample.dosomething() trace:<traceback object at 0x000001C08CF32F88> File "C:/Users/xxx/PycharmProjects/Test1/test.py", line 10, in dosomething __exit()__ is call! x = 1/0 ZeroDivisionError: division by zero
从结果可以看出, 在执行到dosomethong时就发生了异常,然后将异常传给了__exit__(), 依据上面的官方说明:如果有异常发生,并且该方法希望抑制异常(即阻止它被传播),则它应该返回True。否则,异常将在退出该方法时正常处理。当前__exit__并没有写明返回True,故会抛出异常,也是合理的,但是正常来讲,程序应该是不希望它抛出异常的,这也是调用者的职责,我们将再次修改__exit__, 将其返回设置为True,
3. 有异常发生时,不再抛出异常的例子:
在上面的例子上做点修改.
#!/user/bin/env python3 #-*- coding:utf-8 -*- 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: sample.dosomething() >> __enter__() is call! __exit__() is call! type:<class 'ZeroDivisionError'> value:division by zero trace:<traceback object at 0x000001C94E592F88> __exit()__ is call!
从结果看,异常抛出被抑制了,符合预期。
原文链接:https://blog.csdn.net/lxy210781/article/details/81176687