文件打开与关闭:open()函数详解

文件操作,说白了就是跟磁盘上的数据打交道。我刚开始学Python时,觉得文件操作特别简单——不就是打开、读写、关闭嘛。但后来在项目中踩过几次坑,才发现这里面的门道还真不少。

今天咱们就好好聊聊文件打开与关闭这件事。你想想看,如果连门都开不对,后面的操作肯定全乱套。

open()函数:打开文件的第一把钥匙

Python里打开文件用的是open()函数。它的基本用法很简单:

file_object = open(file_name, mode)

第一个参数是文件名,第二个参数是打开模式。返回值是一个文件对象,后面所有的读写操作都靠它。

我个人习惯把文件名写成字符串,路径用正斜杠或者双反斜杠。比如:

f = open('data.txt', 'r')      # 当前目录
f = open('./data/info.txt', 'r')  # 相对路径
f = open('C:/Users/data.txt', 'r') # 绝对路径

注意:Windows路径里的反斜杠是转义符,要么用双反斜杠\\,要么用正斜杠/。我曾经因为这个bug排查了半小时,最后发现是路径写错了。

文件模式:r/w/a/r+/w+/a+ 到底怎么选?

模式这块,很多新手容易搞混。我整理了一张表,你一看就明白:

模式 可读 可写 文件存在 文件不存在 写入位置
r ✅ ❌ 正常打开 报错 -
w ❌ ✅ 清空内容 新建文件 文件开头
a ❌ ✅ 追加写入 新建文件 文件末尾
r+ ✅ ✅ 正常打开 报错 文件开头
w+ ✅ ✅ 清空内容 新建文件 文件开头
a+ ✅ ✅ 追加写入 新建文件 文件末尾

我来逐个说说我的使用心得:

  • r模式:最常用。只读不写,安全可靠。我一般读取配置文件、日志分析都用它。
  • w模式:写入新内容。注意它会清空原文件!我在项目中用它来生成报表,每次重新生成。
  • a模式:追加写入。日志文件的最佳选择。不会覆盖已有内容,新内容直接加在末尾。
  • r+模式:可读可写,但不会清空文件。说实话,这个模式我很少用,因为容易搞乱文件指针的位置。
  • w+模式:可读可写,但会清空。我一般用来测试,或者需要同时读写新文件时用。
  • a+模式:可读可写,追加模式。适合需要读取历史记录又要追加新数据的场景。

避坑指南:我曾经在项目里用w模式打开了一个重要的配置文件,结果文件内容全没了。从那以后,我养成了一个习惯——写文件前先确认模式,尤其是w和a的区别,一定要想清楚。

文件关闭:为什么不能忘?

打开文件后,一定要记得关闭。为什么?

第一,操作系统对同时打开的文件数量有限制。你打开不关,迟早会达到上限。

第二,写入操作可能还在缓冲区里,没真正写到磁盘。关闭文件时,缓冲区才会被刷新。

第三,文件锁的问题。有些系统里,文件被打开后其他程序就不能操作了。

正确的做法是:

f = open('data.txt', 'r')
# 读写操作...
f.close()

但这样写有个问题——如果中间代码报错了,close()可能根本执行不到。嗯,这里要注意。

with语句:最优雅的打开方式

Python提供了with语句,它是个上下文管理器。说白了,就是帮你自动处理打开和关闭。

with open('data.txt', 'r') as f:
    content = f.read()
    # 在这里做读写操作
# 离开with块后,文件自动关闭

你想想看,这样写多省心。不用记着close(),也不用担心异常导致文件没关闭。

我个人强烈建议:所有文件操作都用with语句。除非你有特殊需求,否则别用裸的open()。

小技巧:with语句可以同时打开多个文件:

with open('a.txt', 'r') as f1, open('b.txt', 'w') as f2:
    data = f1.read()
    f2.write(data)

这样两个文件都会在退出with块时自动关闭。

上下文管理器:with背后的原理

你可能好奇,with到底是怎么工作的?其实它依赖两个特殊方法:__enter__和__exit__。

当你写with open(...) as f时:

  1. Python调用open()返回的文件对象的__enter__方法
  2. __enter__返回文件对象本身,赋值给f
  3. 执行with块里的代码
  4. 无论代码是否报错,都会调用__exit__方法
  5. __exit__里执行了f.close()

说白了,这就是个语法糖。但Python的语法糖,用起来就是香。

你也可以自己写上下文管理器:

class MyFile:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        # 返回False表示不处理异常,让上层处理
        return False

# 使用
with MyFile('test.txt', 'r') as f:
    print(f.read())

不过说实话,日常开发中直接用open()配合with就够了。自己写上下文管理器,一般是在封装数据库连接、网络连接这类资源时才会用到。

总结一下

文件打开与关闭,核心就三点:

  • 选对模式:r/w/a/r+/w+/a+,根据需求来
  • 记得关闭:或者直接用with语句
  • 注意异常:文件不存在、权限不够、磁盘满了,这些都要考虑

我在项目中见过太多因为文件没关闭导致的问题了。有的程序跑着跑着报"Too many open files",有的数据写到一半断电了全丢了。这些坑,咱们能避就避。

下一节咱们聊聊文件读写操作,到时候会用到今天讲的这些知识。先把基础打牢,后面才能玩得转。