Python 文件与 IO 实战:pathlib 与上下文管理器

举报
yd_269685704 发表于 2026/11/03 10:04:48 2026/11/03
【摘要】 用 pathlib 替代字符串路径拼接,用 with 语句保证文件正确关闭,用临时文件加原子替换实现安全写文件,并把成对资源操作抽成自定义上下文管理器。

为什么文件操作总在细节处翻车

写 Python 的人都处理过文件:读配置、写日志、批量改名、导出报表。代码跑起来不难,难的是跑得稳:路径在 Windows 和 Linux 下表现不同,文件忘记关闭导致句柄泄漏,程序中途异常让写了一半的文件留在磁盘上。这些问题很少在第一次运行时暴露,却会在数据量变大、运行时间变长之后集中出现。本文用 pathlib 和上下文管理器把文件 IO 的常见坑一次性讲清楚。

用 pathlib 代替字符串拼接路径

过去拼接路径常用字符串加斜杠,换个操作系统就可能出错。pathlib 的 Path 对象把路径当作结构化数据处理:斜杠运算符拼接、parent 取父目录、suffix 取扩展名、stem 取不带扩展的文件名,语义清晰且跨平台一致。

from pathlib import Path

base = Path("data") / "reports"
base.mkdir(parents=True, exist_ok=True)
for p in base.glob("*.csv"):
    print(p.stem, p.suffix, p.stat().st_size)

几个容易忽略的点:mkdir 记得带 parents=True 和 exist_ok=True,避免中间目录不存在或目录已存在时报错;glob 只匹配当前目录,需要递归时用 rglob;判断文件是否存在用 exists,但存在不代表可读,真正读写时仍要处理权限异常。

上下文管理器:把关闭交给语法

open 返回的文件对象必须关闭,否则写缓冲的数据可能滞留在内存,读句柄也可能耗尽。with 语句保证离开代码块时一定执行关闭,哪怕中间抛出异常:

from pathlib import Path

src = Path("input.txt")
with src.open("r", encoding="utf-8") as f:
    lines = [line.strip() for line in f if line.strip()]

这里有两个细节值得养成习惯。第一,显式写 encoding="utf-8",不要依赖系统默认编码,否则同一份代码在不同机器上读中文可能得到不同结果。第二,用迭代器逐行读而不是一次性 read,处理大文件时内存占用是常数级别。

写文件的安全姿势:先写临时文件再替换

直接覆盖原文件是最危险的写法:一旦写入中途崩溃,原文件和新内容可能同时丢失。更稳妥的做法是先写入同目录下的临时文件,写完并刷盘后再用 os.replace 原子替换:

import os
from pathlib import Path

def atomic_write(path, text):
    path = Path(path)
    tmp = path.with_suffix(".tmp")
    with tmp.open("w", encoding="utf-8") as f:
        f.write(text)
        f.flush()
        os.fsync(f.fileno())
    os.replace(tmp, path)

os.replace 在同一文件系统内是原子操作,替换瞬间要么是旧文件,要么是新文件,不会出现半成品。临时文件要放在目标文件同一目录,否则跨文件系统时 replace 会退化成复制,失去原子性。

自定义上下文管理器:把成对操作包起来

除了文件,锁、临时目录、计时、切换工作目录这类成对出现的操作,都值得写成上下文管理器。contextlib.contextmanager 可以用一个生成器函数快速实现:

import time
from contextlib import contextmanager

@contextmanager
def timer(name):
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"{name} 耗时 {time.perf_counter() - start:.3f}s")

yield 之前是进入逻辑,yield 之后是退出逻辑,try/finally 保证退出逻辑一定执行。把这类样板代码收进工具模块,业务代码里只剩一行 with,阅读和维护成本都会下降。

一个可直接复用的小结

  • 路径用 pathlib,不要手写斜杠拼接。
  • 读写文件永远用 with 打开,并显式指定 encoding。
  • 大文件逐行处理,不要一次性读入内存。
  • 覆盖写文件时用临时文件加 os.replace,避免半成品。
  • 成对的资源操作抽成上下文管理器,让语法替你记得收尾。

文件 IO 没有太多高深概念,胜负手全在习惯。把上面这几条变成默认写法,程序在数据量和运行时间增长之后依然稳得住。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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