写代码的时候你肯定遇到过这种情况。打开一个文件,读数据,处理完。然后直接关了程序。或者脚本跑完了,文件好像没关。第二天再跑程序,报错了。说文件被占用。你心里那个烦啊。
我见过一个同事,他写爬虫。爬了好多网页,每爬一个就写一个文件。代码里全是 open() 和 close()。有一次他跑了半天的数据,电脑突然断电了。等他重开电脑,发现爬下来的文件全坏了。因为文件没来得及关,内容根本没写进去。他坐在工位上,脸都白了。
这就是忘了关文件的代价。你可能会说,我记性好,每次都记得写 file.close()。但人不是机器,总有走神的时候。有句话很贴切,你不可能百分百保证每个文件都关闭。就像出门锁门,你总有一两次忘记吧。
Python 的设计者考虑到了这个问题。他们给了我们一个工具,叫 with 语句。用法很简单,就是把你对文件的操作包在一个块里。这样说你可能觉得抽象,直接看代码。
不用 with 语句的写法是这样:
file = open('data.txt', 'r') content = file.read()
file.close()
用了 with 语句,变成这样:
with open('data.txt', 'r') as file: content = file.read()
区别在哪?用 with 语句,Python 会在你离开这个代码块的时候自动把文件关掉。不用你写 file.close()。不管你是因为代码跑完了,还是遇到错误跑飞了,它都会去关。它永远不会忘。
这个特性,程序员叫它上下文管理器。你不用管这个术语,你只需要知道 with 语句像是一个保姆,你只管用文件,它负责打扫卫生。
有人担心,多写个 with 是不是更慢。不用担心。文件操作本身就是慢的。哪怕你用最快的固态硬盘,打开和关闭文件也要花几毫秒。而 Python 用 with 关闭文件,几乎不消耗时间。你感觉不到差异。
还有人说,我非要用 try...finally 来保证关闭文件。这当然行。但代码会变长,也容易写错。比如你 try 里面调用了函数,那个函数出错了,你 finally 里面还得记得处理异常。用 with 语句,一行搞定所有烦恼。
我教过一个学生,刚学完文件操作。他写了一个分析日志的程序。日志特别大,几百兆。他打开文件后用了 for 循环一行行读。如果数据有问题,他直接 break 退出循环。但他没写 close。然后程序报错了,他还以为是数据格式的问题。检查了半天,最后发现是文件句柄耗尽了。他问我怎么办。我说,把 open 改成 with open。他改完,问题就消失了。
从此以后,他写文件操作只用 with。他还说,用 with 就像有了自动刹车,再也不怕撞车了。
你可能会说,我平时写的都是小脚本,就几个文件。关了忘了关也问题不大。但你有没有想过,如果代码要部署到服务器上跑。服务器是常年开着的。你写一个定时任务,每天凌晨跑一次。你忘了关文件,一天积累一点,七天以后文件句柄就用完了。到时候整个任务挂掉,你才追悔莫及。
我建议你把 with 语句当成习惯。不要依赖自己的记忆。人的记忆是靠不住的。写一行 with 语句,你能省下十行的捉虫时间。
这还不光是对自己好。你在团队里写代码,别人会看你的代码。如果别人看到你每个文件操作都用了 with,他会觉得你很专业。如果你没用,他可能会觉得你是个新手。代码不只是给机器看的,也是给人看的。规范易读的代码,就是一种沟通。
另外我告诉你一个冷知识,with 语句不光能管文件。它还能管线程锁,网络连接,数据库游标。凡是需要打开和释放的东西,都能用 with 来管理。你学会了这一个技巧,就能应付很多场景。
最后,我想说,写代码这件事,重要的不是你有多厉害,而是你犯的错误有多小。用 with 语句,就是让你彻底忘掉关文件的烦恼。你只要记得写一个关键字,剩下的事情交给 Python 就行。文件关闭这种烂摊子,让 with 来帮你收拾吧。