人人都会AI编程

12.3 上下文管理器操作文件的最佳实践

更新时间:2026-07-12

在 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