python中with的用法
一、文件操作
#自行车
f=open("filename") f.write() f.close()
上述代码存在的问题:
(1)直接open()打开需要手动关闭,并且容易忘记关闭
(2)当文件操作出现异常导致程序提早离开,而没有执行关闭文件操作
#小轿车
try: f=open("xxx") f.write() #文件操作 except: do something finally: f.close()
虽然功能完善了,但是代码这么冗余,也太笨了吧
#特斯拉
with open("xxx") as f: f.write() #文件操作
一个with代码块直接解决一切,无论怎样的方式退出代码块都会自动关闭文件
问题来了,
with为什么可以做到自动关闭文件?
with为什么即便文件操作中出现异常也可以正常关闭文件?with语句和原始open同样返回一对象,有什么不一样?
二、with原理
__enter__()方法:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。
__exit__()方法:当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法
class Test(object): def __enter__(self): print("执行了 __enter__方法") return "enter返回的内容" def __exit__(self, type, value, trace): print("执行了 __exit__方法") with Test() as test: print("test:", test)
运行结果:
执行了 __enter__方法 test: enter返回的内容 执行了 __exit__方法
执行过程:
with后面的Test()语句执行、enter()执行
enter()返回值返回给as后面的变量test
执行with语句中间代码块打印变量test、
中间代码块执行完后执行__exit__()
推断:自动关闭文件是在__exit__()中调用文件关闭方法
三、实现支持with的类
open支持with语法,就是因为内部实现了双下enter和双下exit
想让类来使用with就必须支持with语法,即内部实现双下enter和双下exit,如下:
import sys import time import threading class Spinner: busy = False delay = 0.5 @staticmethod def spinning_cursor(): while 1: for cursor in '|/-\\': yield cursor def __init__(self, delay=None): print(111) self.spinner_generator = self.spinning_cursor() if delay and float(delay): self.delay = delay def spinner_task(self): while self.busy: print(333) sys.stdout.write(next(self.spinner_generator)) #IO print(999) sys.stdout.flush() time.sleep(self.delay) #IO print(666) sys.stdout.write('\b') sys.stdout.flush() print(777) def __enter__(self): print(222) self.busy = True threading.Thread(target=self.spinner_task).start() def __exit__(self, exception, value, tb): self.busy = False time.sleep(self.delay) if exception is not None: return False with Spinner(): print(444) t1 = time.time() while time.time()-t1 < 2: print(555) time.sleep(1) #io print('完蛋了')
111 # 执行__init__ 222 # 执行__enter__ 333 # 执行任务spinner_task线程1 444|999 # 遇到write的io,执行另外的线程2,线程1的io完毕,立即切换回去继续执行线程1输出‘|’ 555 # 线程1遇到sleep的io立即切换线程2,走到sleep又io,切换回线程1 666 777 333 /999 完蛋了 555 666 777 333 -999 666 777 333 \999 完蛋了 # 时间大于2,线程2的while执行完毕,即with代码块执行完毕,开始执行__exit__,设置退出标志 666 777 # 线程1输出完毕,while遇到退出信号也完毕了,大家都完了
参考;https://blog.csdn.net/win_turn/article/details/106071154