用os.replace()替换同目录临时文件是最可靠方式:先写临时文件并fsync,再原子替换;需避免跨分区、处理Windows权限错误、校验磁盘空间,且write_text()不保证原子性。

用 os.replace() 替换同目录临时文件是最可靠方式
直接 open(path, 'w') 覆盖写入没有原子性:程序在 f.write() 中途崩溃,原文件可能被截断或残留半截内容,且无法恢复。真正安全的做法是先写入临时文件,再用 os.replace() 原子替换——这个系统调用在 POSIX 和 Windows 上都保证“全有或全无”,只要源和目标在同一个文件系统。
-
tempfile.NamedTemporaryFile(delete=False, dir=os.path.dirname(path))创建与目标同目录的临时路径,避免跨分区导致os.replace()退化为复制+删除 - 写完必须调用
f.flush()和os.fsync(f.fileno()),否则数据可能还卡在内存缓存里,替换后仍是空或旧内容 - 别用
shutil.move()或os.rename():前者不保证原子,后者在 Windows 上对已存在目标文件可能失败
Windows 下 os.replace() 会因文件被占用而抛 PermissionError
如果目标文件正被记事本、Excel 或其他进程以独占方式打开,os.replace() 在 Windows 上会直接报 PermissionError: [WinError 5],而不是静默失败。这容易让脚本意外中断。
- 读取方应显式声明共享权限(如用
win32file.CreateFile(..., win32file.FILE_SHARE_READ)),而非依赖默认行为 - 写入方可捕获异常并重试,例如循环 3 次、每次间隔 0.1 秒:
for _ in range(3): try: os.replace(tmp, target); break except PermissionError: time.sleep(0.1) - 避免长期持有
open(target, 'r')句柄;读取完成后立即关闭,不要让它成为写入阻塞源
pathlib.Path.write_text() 是单行文本的快捷方案,但不等于原子写
对小配置文件、日志行这类纯文本场景,Path('cfg.json').write_text('{"a":1}') 确实简洁安全:它自动处理编码、刷新、关闭,且内部使用 'w' 模式。但它**不保证原子性**——底层仍是直接覆盖写原文件。
- 若需原子性,仍得走临时文件 +
os.replace()流程;write_text()仅解决资源泄漏和编码问题 - 追加写(
'a')无法用单行闭环实现,必须搭配with open(..., 'a') as f:手动管理 - 二进制内容必须用
write_bytes(),传字符串会触发TypeError
大文件写入时临时文件策略要谨慎
几百 MB 甚至 GB 级文件频繁生成临时副本,会放大磁盘 I/O 和空间压力,尤其在容器或低配机器上容易触发 OSError: No space left on device。
立即学习“Python免费学习笔记(深入)”;
- 写入前先校验可用空间:
shutil.disk_usage(os.path.dirname(path)).free,留出至少 1.5 倍余量 - 大文件建议分块写入临时文件,并在
os.replace()前做内容校验(如hashlib.blake2b()对比) - 若业务允许,考虑追加写(
'a')替代全量覆写,避开原子性难题
fsync 调用时机、Windows 文件占用处理——这三个点任一遗漏,原子性就形同虚设。实际部署时,务必在目标环境(尤其是 Windows + NFS)上跑一次断电/kill -9 模拟测试。


















