news 2026/10/17 5:27:57

关于Python的各种文件操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
关于Python的各种文件操作

前言


一说到「文件操作」,多数人第一反应是open()读和写。但真实项目里,读写只占一半,另一半是路径拼接、判断存在、复制、移动、重命名、删除、取文件信息。这些操作分散在os、os.path、shutil、pathlib几个模块里,新手常常凭印象拼一个函数名出来,跑起来才发现根本没有这个函数。


另一个常见误解是把pathlib和os.path当成「新写法 / 旧写法」二选一。实际上两者定位不同:os.path处理的是字符串路径,pathlib处理的是路径对象,它们调用同一套底层系统接口,官方文档专门列了对照表。选哪个更多是风格问题,混用也不会出错。


本文按「路径怎么拼 → 文件怎么判断、复制、移动、删除 → 信息怎么取」的顺序,把常用操作和它们的准确签名梳理一遍。示例面向 Python 3.8+,涉及 3.9+ 才有的方法时会单独注明。


一、路径拼接与规范化


拼接路径不要手写字符串加号,因为 Windows 用反斜杠、类 Unix 用正斜杠:


# 适用于 Python 3.8+
import os

path = os.path.join("reports", "2026", "q1.csv")
print(path) # Windows 上是 reports\2026\q1.csv

os.path常用的几个函数:




函数作用



os.path.join(*parts)用当前系统的分隔符拼接路径

os.path.basename(path)取最后一段(文件名)

os.path.dirname(path)取最后一段之前的部分

os.path.splitext(path)拆成(主干, 扩展名)二元组

os.path.abspath(path)转成绝对路径(不解析符号链接)

os.path.realpath(path)解析符号链接后的真实路径

os.path.normpath(path)折叠..与多余分隔符



pathlib把同样的功能做成了方法。官方的对照关系是:os.path.join()对应PurePath.joinpath(),os.path.abspath()对应Path.absolute(),os.path.realpath()对应Path.resolve()。用/运算符拼接最直观:


# 适用于 Python 3.8+
from pathlib import Path

p = Path("reports") / "2026" / "q1.csv"
print(p.name) # q1.csv
print(p.suffix) # .csv
print(p.stem) # q1
print(p.parent) # reports/2026

有一点值得注意:Path.absolute()会保留路径里的..,而os.path.abspath()会把..消掉,这两者在语义上有细微差别,迁移代码时不要想当然地替换。


二、复制、移动、改名、删除


shutil(shell utilities)负责「文件/目录级」的搬运。它的签名必须写准:


# 适用于 Python 3.8+
import shutil

shutil.copyfile(src, dst) # 只复制内容,dst 必须是完整文件名
shutil.copy(src, dst) # 复制内容 + 权限位;dst 可以是目录
shutil.copy2(src, dst) # 在 copy 基础上再尽量保留元数据
shutil.copytree(src, dst) # 递归复制整个目录
shutil.move(src, dst) # 移动(同盘相当于改名,跨盘是复制后删除)
shutil.rmtree(path) # 递归删除目录树,危险

copyfile与copy的区别常被忽略:copyfile的第二个参数必须是完整的文件名,而copy允许给一个目录,此时会把源文件名接到该目录下。copy2额外用copystat()复制元数据,但官方明确说它也不能复制全部元数据——不要以为copy2就是无损克隆。


单纯改名用os.rename();如果目标存在需要覆盖,用os.replace():


# 适用于 Python 3.8+
import os

os.rename("old.txt", "new.txt") # 目标已存在时,Windows 上会报错
os.replace("draft.txt", "final.txt") # 目标存在则原子覆盖

删除分三种,务必分清:


# 适用于 Python 3.8+
import os

os.remove("a.txt") # 删文件;等价别名 os.unlink()
os.rmdir("emptydir") # 只能删空目录
os.removedirs("a/b/c") # 自底向上删,目录不空就停

os.rmdir()遇到非空目录会抛OSError,这是它和rmtree最大的区别——也是保护机制,别急着改成rmtree。


三、判断存在性与取文件信息


os.path系列的判断函数返回布尔值:




函数判断内容



os.path.exists(p)路径是否存在(文件或目录都算)

os.path.isfile(p)是否是普通文件

os.path.isdir(p)是否是目录

os.path.islink(p)是否是符号链接

os.path.getsize(p)文件字节数(目录上的结果依系统而定)



pathlib里的对应写法是Path.exists()、Path.is_file()、Path.is_dir()、Path.is_symlink()、Path.stat()。用stat()拿到的是os.stat_result,可以读.st_size、.st_mtime等字段:


# 适用于 Python 3.8+
from pathlib import Path

p = Path("data.csv")
if p.is_file():
info = p.stat()
print(f"占用 {info.st_size} 字节")

实战:把一个目录里的文件按扩展名归档


这段脚本把inbox里的文件按扩展名移动到对应子目录,演示了路径拼接、存在判断、目录创建和移动的组合用法:


# 适用于 Python 3.8+
from pathlib import Path
import shutil

def archive(inbox="inbox"):
root = Path(inbox)
for entry in root.iterdir():
if not entry.is_file():
continue
ext = entry.suffix.lstrip(".").lower() or "noext"
target_dir = root / ext
target_dir.mkdir(exist_ok=True) # 已存在不报错
shutil.move(str(entry), str(target_dir / entry.name))

if __name__ == "__main__":
archive()

两个细节:Path.mkdir(exist_ok=True)才能对已存在的目录静默通过,默认exist_ok=False会抛FileExistsError;shutil.move接受的是路径,传Path对象大多数时候没问题,但为了行为稳定,显式str()一下更保险。


常见坑点


1. 用字符串加号拼路径


❌"src/" + name,在 Windows 上可能拼出混用分隔符的路径。 ✅os.path.join("src", name)或Path("src") / name。


2. 把shutil.copyfile的第二个参数写成目录


❌shutil.copyfile("a.txt", "backup/"),会抛IsADirectoryError。 ✅ 要写目录就用shutil.copy("a.txt", "backup/")。


3. 用os.rmdir删非空目录


❌ 对非空目录调用os.rmdir(),报OSError: Directory not empty。 ✅ 确认要整树删除时用shutil.rmtree();只是想删空目录才用rmdir。


4. 判断存在后立刻操作,中间目录被删


❌if os.path.exists(p): open(p),两步之间文件可能已经消失。 ✅ 直接操作并捕获FileNotFoundError,也就是常说的「宁求原谅不求许可」。


5. 以为copy2保留全部元数据


❌ 认为copy2能把所有者、ACL 都带过去。 ✅ 官方文档明确说明copy2也不能复制全部元数据,需要时用系统级工具或copystat单独补。


6.os.rename跨盘失败


❌ 用os.rename()在 C 盘和 D 盘之间移动文件,报错。 ✅ 跨卷移动用shutil.move(),它内部会退化成「复制 + 删除」。


7.mkdir少写parents/exist_ok


❌Path("a/b/c").mkdir()在a不存在时抛FileNotFoundError。 ✅Path("a/b/c").mkdir(parents=True, exist_ok=True)。


8. 在os.walk里边遍历边删目录


❌ 遍历中直接shutil.rmtree(dirnames),导致后续遍历踩到不存在的路径。 ✅ 先收集完整路径列表,遍历结束后再统一删除。


总结




需求首选写法备注



拼接路径os.path.join/Path / name不要用字符串加号

复制文件shutil.copy/shutil.copy2copyfile要求完整目标名

递归复制shutil.copytreedirs_exist_ok控制目标存在行为

改名 / 覆盖os.rename/os.replacereplace原子覆盖

删文件os.remove别名os.unlink

删空目录 / 整树os.rmdir/shutil.rmtreermtree危险,二次确认

存在性os.path.exists/Path.exists判断与操作之间别留缝



文件操作本身是简单 API 的堆叠,真正容易出事的地方是边界:目标已存在、目录非空、跨卷移动、路径分隔符。把这几类情况的预期行为想清楚,代码就稳了。




版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 5:27:52

纯前端代码模板管理器:CLI与VS Code插件双模实践

1. 项目概述:一个被误读的CLI工具命名陷阱 “claude-code-templates”这个标题,第一眼容易让人联想到Anthropic家的Claude大模型——毕竟现在满屏都是“ClaudeXXX”的命名套路。但实际拆开看,它根本不是什么AI代码生成器,也不是官…

作者头像 李华
网站建设 2026/10/17 5:27:18

金融服务微服务架构设计:API网关、幂等与对账实战

1. 这个金融服务项目到底要解决什么问题先说清楚一点:金融服务这个词听起来很大,但在实际工程里,它落到最后就是一系列需要被稳定、安全、高效地组装起来的业务能力——开户、余额查询、充值、提现、转账、交易记录、账单、风控拦截、数据分析。我这次分享的项目,就是围绕这些能…

作者头像 李华
网站建设 2026/10/17 5:25:44

client-go Clientset源码拆解:从结构到构造函数全解析

前面用客户端连集群,大概每个人都是从k8s.io/client-go的Clientset开始的。但你有没有想过,为什么Clientset里没有Pods()方法,而要先CoreV1()再拿Pods()?为什么NewForConfig传一个*rest.Config就能拿到十几个资源组的客户端&#…

作者头像 李华
网站建设 2026/10/17 5:25:35

Claude Code模板工程化指南:从CLAUDE.md到Hooks的完整配置

1. 从零搭建:Claude Code模板到底是什么先说结论:Claude Code 模板不是一套花架子配置,而是一组你整理好的工程化文件,用来告诉 Claude Code 这个 AI 编程助手——你的项目是什么结构、代码风格是什么、哪些事能做哪些事不能碰、以…

作者头像 李华
网站建设 2026/10/17 5:24:52

Androidfby反编译工具实战:APK逆向全链路与避坑指南

简介:Androidfby反编译工具面向Android开发者与逆向工程学习者,用于解析APK内部结构,查看Java源代码、资源文件与XML配置,便于分析应用工作原理或在修改、优化、调试APK时提供辅助。压缩包共488个文件,约8.84MB&#x…

作者头像 李华
网站建设 2026/10/17 5:24:32

AI 70年发展史:从图灵测试到大模型,零基础也能看懂

如果你最近被“AI写论文”“AI画图”“AI做视频”这类新闻轰炸过,脑子里大概率冒出两个问题:这些东西是这两年突然冒出来的吗?我一个没学过编程的人,能不能搞懂它到底怎么来的?这篇文章就是想用零基础也能听明白的大白…

作者头像 李华