在 Python 中操作文件,最推荐的方式就是使用 with 语句。它让你不必手动调用 close(),也能保证文件在任何情况下都被正确关闭,既安全又简洁。
为什么不用传统方式?
很多人一开始学文件操作是这样写的:
f = open('data.txt', 'r')
content = f.read()
f.close()
看起来没什么问题,但如果 f.read() 抛出异常,程序直接跳到异常处理,f.close() 就永远不会执行。文件没关闭,可能导致数据丢失、资源泄漏,甚至其他程序无法访问该文件。
虽然可以用 try...finally 解决:
f = open('data.txt', 'r')
try:
content = f.read()
finally:
f.close()
但这种写法冗余,而且容易忘。with 语句就是来优雅地解决这个问题的。
with 语句的基本用法
with open('data.txt', 'r') as f:
content = f.read()
- 当程序进入
with块时,open()返回的文件对象会被赋值给变量f。 - 当程序离开
with块时(无论是正常结束还是发生异常),文件对象的exit方法会被自动调用,执行关闭操作。
它背后的机制
open() 函数返回的文件对象,实现了上下文管理器协议,也就是两个魔法方法:
enter():进入with块时调用,返回文件对象本身(赋给as后面的变量)。exit(exc_type, exc_val, exc_tb):离开块时调用,负责关闭文件。即使块中发生了异常,三个异常参数也会被传入,你可以在这里决定是否压制异常。
内置的文件对象在 exit 方法里调用了 close(),所以我们只管用,关闭的事它都处理好了。
常见最佳实践场景
1. 一次性读取或写入
# 读取全部内容
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
# 写入内容(自动覆盖)
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('Hello, Python')
2. 逐行处理大文件
with open('large_log.txt', 'r', encoding='utf-8') as f:
for line in f:
process(line) # 每次只保留一行在内存中,非常友好
对于几百 MB 甚至上 GB 的日志文件,这样处理内存占用极低。
3. 同时处理多个文件
with open('source.txt', 'r') as src, \
open('destination.txt', 'w') as dst:
dst.write(src.read())
可以在一个 with 语句中打开多个文件,用逗号分隔,所有文件都会在离开时自动关闭。
4. 结合 open 的完整参数
with open('data.bin', 'rb', buffering=4096) as f:
while chunk := f.read(1024):
handle(chunk)
二进制读、指定缓冲区大小都可以按需设置,with 只管资源释放。
不只是文件:任何需要“用完即关”的地方
上下文管理器不仅用于文件。数据库连接、网络套接字、锁等资源都应该用 with 管理:
import sqlite3
with sqlite3.connect('app.db') as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
连接会在离开 with 块时自动提交并关闭(或回滚,取决于对象实现)。
总结:为什么这是最佳实践
- 安全:文件一定被关闭,即使中间出错。
- 简洁:少写
try...finally,代码意图更清晰。 - 资源友好:避免文件句柄泄露,防止“Too many open files”错误。
- 习惯养成:一旦习惯
with,你会自然地对任何需要“打开/关闭”的资源都统一处理,大大降低代码中的资源管理隐患。
记住:在 Python 中,凡是能放进 with 的资源,就不要自己手动 close。