Python 文件读写与数据持久化实战

Daoman Python AI 的后端开发或模型训练中,我们经常需要处理数据的持久化(Persistence)。所谓持久化,是指将数据从易失的存储介质(通常是内存)转移到可以长久保存的介质(通常是硬盘、SSD)中。实现这一目标最直接、最标准的方式,就是通过文件系统将数据保存到文件里。


1. 理解文件系统的抽象层

计算机的文件系统是一套成熟的“数据管家”方案。它用“文件”“树形目录”的简单逻辑概念,彻底屏蔽了硬盘磁道寻址、SSD磨损平衡、USB协议交互这些复杂的底层物理细节。

作为应用层开发者,你完全不需要提前找硬盘的空白物理扇区,也不用操心断电后的数据校验问题——文件系统会自动分配空间、管理索引、处理故障容错。你只需要通过绝对路径/相对路径+文件名,就能轻松完成“存东西、取东西”的操作,这极大降低了存储开发的入门门槛。


2. 打开与关闭文件:open() 函数核心解析

Python 中操作文件的唯一(官方推荐)入口是内置函数 open()。通过传入不同的操作模式字符串,你可以控制文件的读写权限、处理方式(文本/二进制)、更新权限等。

2.1 常用操作模式速查

整理了高频场景下的模式组合,避免记混单字符的含义:

操作模式组合核心权限关键细节高频场景
'r'/'rt'只读文本默认模式;文件不存在直接报错读取配置、训练数据预览
'w'/'wt'覆盖写文本文件不存在则创建;存在则清空所有内容重写临时生成新的配置/日志备份
'a'/'at'追加写文本文件不存在则创建;存在则在末尾追加,光标默认在末尾爬虫数据入库、训练实时日志
'x'/'xt'排他写文本文件不存在则创建;存在则直接报错防止多人/多进程误覆盖关键文件
'rb'只读二进制直接读原始字节流;无需编码参数读取图片、模型权重(.pth/.pt)、压缩包
'wb'覆盖写二进制直接写原始字节流保存图片、模型、二进制日志
'r+'/'w+'更新文本同时支持读写;w+ 会先清空旧文件需同时读取和修改的简单配置

🚩 新手避坑指南(必看) 处理中文文本、JSON/YAML等结构化文本文件时,务必显式指定 encoding='utf-8'!不要依赖操作系统的默认编码(比如 Windows 下的 GBK、Mac/Linux 早期可能用的 Latin-1),否则 90% 以上的概率会遇到经典的 UnicodeDecodeErrorUnicodeEncodeError


3. 文本读写的工业级实践

虽然 Python 提供了 file.close() 手动关闭文件的方法,但在生产环境或团队协作代码中,100% 推荐使用 with 关键字(上下文管理器)

它有两个不可替代的优势:

  1. 自动安全释放资源:即使读写过程中发生了 ValueErrorIOError 甚至是系统级异常,文件句柄也会被立刻释放,不会造成资源泄漏(资源泄漏在高并发服务中会导致“Too many open files”报错,直接挂服务)。
  2. 代码可读性更强:缩进块明确了“文件操作的有效范围”,团队成员一眼就能看懂哪里在操作文件。

3.1 读取文本文件的三种主流方式

根据文件大小和业务需求,选择最适合的读取方案,不要盲目用全量读取

# 所有示例都统一使用 with 上下文管理器 + utf-8 编码
with open('致橡树.txt', 'r', encoding='utf-8') as file:
    # ========== 方式 A:逐行迭代(⭐️⭐️⭐️ 工业级首选) ==========
    # 每次只加载一行到内存,内存占用永远是 O(1)
    # 适合处理 GB 级的日志、千万条的爬虫数据
    print("=== 逐行迭代(strip去除换行/空格) ===")
    for line in file:
        # 记得用 strip() 或 rstrip('\n') 去掉自动带的换行符
        print(line.strip())

    # ========== 方式 B:全量读取(适合 <10MB 的文件) ==========
    # 一次性加载所有内容到内存,得到一个完整的字符串
    # 注意:上面的 for 循环已经把文件指针移到末尾了,需要重置
    file.seek(0)  # 把文件指针移回文件开头
    print("\n=== 全量读取 ===")
    full_content = file.read()
    print(full_content[:100])  # 只打印前100个字符演示

    # ========== 方式 C:列表读取(适合需要索引某一行的场景) ==========
    # 把每行作为一个元素存入列表,内存占用和全量读取差不多
    file.seek(0)
    print("\n=== 列表读取(打印第3行) ===")
    lines_list = file.readlines()
    if len(lines_list) >= 3:
        print(lines_list[2].strip())

3.2 写入与追加数据

根据是否保留旧内容,选择 'w'(覆盖)或 'a'(追加)模式:

# ========== 场景 1:覆盖写(新建或清空) ==========
with open('致橡树-精简版.txt', 'w', encoding='utf-8') as file:
    # write() 不会自动加换行符,需要手动加 \n
    file.write("我如果爱你——\n")
    file.write("绝不像攀援的凌霄花,\n")
    file.write("借你的高枝炫耀自己;")

# ========== 场景 2:追加写(保留旧内容,末尾加新的) ==========
with open('致橡树.txt', 'a', encoding='utf-8') as file:
    # 追加两行赏析内容
    file.write("\n\n--- 诗歌赏析 ---")
    file.write("\n《致橡树》通过木棉与橡树的对话,展现了独立、平等、相互扶持的现代爱情观。")