Python 保存 JSON 时,为什么要先写临时文件再替换

举报
TechCao 发表于 2026/10/29 16:33:01 2026/10/29
【摘要】 直接覆盖 JSON 文件时,异常可能破坏旧数据。本文用 Python 标准库展示“同目录临时文件+os.replace”的保存方法,解释 Windows 文件句柄、flush/fsync、失败清理和并发写入的边界,并提供本地验证思路。

脚本把配置或计算结果保存成 JSON 时,常见写法是直接打开目标文件,再调用 json.dump()。这种写法很短,却有一个容易忽略的风险:以 w 模式打开文件会先截断旧内容。如果数据无法序列化、磁盘写入失败或进程中断,原本可用的文件可能只剩空文件或半段 JSON。

更稳妥的思路,是把“生成新内容”和“替换旧文件”分成两步:先在目标目录创建临时文件,完整写入并关闭它,最后用 os.replace() 替换目标。下面是一个适用于普通本地文件的入门示例,目标目录需要预先存在。

import json
import os
import tempfile
from pathlib import Path


def save_json(path, data):
    path = Path(path)
    temp_path = None
    try:
        with tempfile.NamedTemporaryFile(
            mode="w",
            encoding="utf-8",
            dir=path.parent,
            prefix=f".{path.name}.",
            suffix=".tmp",
            delete=False,
        ) as stream:
            temp_path = Path(stream.name)
            json.dump(data, stream, ensure_ascii=False, indent=2)
            stream.flush()
            os.fsync(stream.fileno())
        os.replace(temp_path, path)
    finally:
        if temp_path is not None:
            temp_path.unlink(missing_ok=True)


save_json("settings.json", {"theme": "dark", "说明": "示例配置"})

这里有四个值得理解的细节。

第一,临时文件放在目标目录。文件系统通常要求原子替换发生在同一文件系统内;如果先写系统临时目录,再移到另一块磁盘,就不能直接套用这个假设。os.replace() 成功时,常见本地文件系统中的读者会看到替换前或替换后的文件,而不是本次写入的半成品。

第二,先退出 with 再替换。这样文件句柄已经关闭,对 Windows 尤其有帮助。delete=False 让临时文件在关闭后仍存在,由后续替换或清理逻辑处理。

第三,flush() 和 fsync() 的职责不同。前者把 Python 缓冲区交给操作系统,后者请求同步文件内容。即便这样,也不能承诺任意存储设备、网络文件系统和断电场景都绝对安全;在部分系统上,还需要同步父目录才能进一步保证重命名元数据的持久性。

第四,失败后尽量清理临时文件。比如把 set 写入 JSON 会触发 TypeError,此时替换尚未执行,旧文件仍保留,finally 会删除本次临时文件。若清理本身失败,仍应让调用方看到异常并检查磁盘状态。

这个方法也有边界:两个进程同时写入时,通常是最后一次替换生效,它不提供事务、合并或并发锁;替换后的文件权限也可能与旧文件不同。涉及共享写入、权限保留或多文件一致性时,需要额外设计,不能只靠一次替换解决。

验证时可以先保存一个有效对象,再尝试保存包含 set 的对象,确认第二次报错后旧 JSON 仍能读取。理解这些边界,比把“原子写入”当作万能保证更有用。

本文为 AI 辅助撰写的原创技术示例,代码已在本地验证正常替换、序列化失败保留旧文件与临时文件清理三个场景。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。