Python 文件路径详解
引言
'我明明把文件放在脚本旁边了,为什么 Python 说找不到?'这是初学者在文件操作中最常遇到的问题之一。归根结底,是因为没有正确理解和。文件操作看似简单,但若不了解背后的机制,很容易掉进坑里。
本文介绍了 Python 文件操作中的核心概念——文件路径。详细讲解了绝对路径与相对路径的区别、跨平台路径分隔符的处理陷阱。对比了传统 os.path 模块与现代 pathlib 模块的使用方式,重点阐述了当前工作目录(CWD)与脚本所在目录(__file__)对路径解析的影响,提供了构建可移植路径的最佳实践方案。

'我明明把文件放在脚本旁边了,为什么 Python 说找不到?'这是初学者在文件操作中最常遇到的问题之一。归根结底,是因为没有正确理解和。文件操作看似简单,但若不了解背后的机制,很容易掉进坑里。
本文将深入浅出地介绍 Python 文件操作的三大核心:文件路径、读取文件、写入文件。学完本文,你将能够自信地处理各种文件读写任务,并能轻松应对跨平台开发中的路径问题。
在开始读写文件之前,Python 必须先知道文件在哪里。这个'位置信息'就是文件路径。如果把计算机比作一个巨大的文件柜,路径就是指引你找到正确抽屉和文件夹的索引。如果路径错了,程序就会抛出 FileNotFoundError。所以,掌握文件路径是文件操作的第一步,也是最容易踩坑的地方。
文件路径是一个字符串,它描述了文件在文件系统中的位置。根据起点不同,路径分为两种:
C:\Users\Alice\Documents\report.pdf/home/alice/Documents/report.pdfC:\Users\Alice,那么相对路径 Documents\report.pdf 就等同于绝对路径 C:\Users\Alice\Documents\report.pdf。. 表示当前目录本身(通常可以省略)。.. 表示上一级目录。例如 ..\data\input.txt 表示从当前目录的父目录下的 data 文件夹中找 input.txt。为什么需要两种路径?
不同操作系统使用不同的符号来分隔目录:
| 操作系统 | 路径分隔符 | 示例 |
|---|---|---|
| Windows | 反斜杠 \ | C:\Users\name |
| Linux/macOS | 正斜杠 / | /home/name |
有个问题!
在 Python 字符串中,反斜杠 \ 是一个转义字符,比如 \n 表示换行,\t 表示制表符。如果你在 Windows 上直接写 "C:\Users\name",Python 会把 \U 解析为 Unicode 转义,导致报错:
>>> path = "C:\Users\name"
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes...
解决方案:
使用双反斜杠:每个反斜杠写两次,转义自身。
path = "C:\\Users\\name\\file.txt"
使用正斜杠:Windows 也接受正斜杠作为路径分隔符,这是最省事的办法。
path = "C:/Users/name/file.txt"
使用原始字符串:在字符串前加 r,让反斜杠保持原样。
path = r"C:\Users\name\file.txt"
os.path 是 Python 早期就有的路径操作模块,提供了一系列函数来处理路径字符串。
常用函数
| 函数 | 描述 |
|---|---|
os.path.join(a, b, ...) | 智能拼接路径,自动添加正确的分隔符。 |
os.path.abspath(path) | 将相对路径转换为绝对路径。 |
os.path.exists(path) | 检查路径是否存在(文件或目录)。 |
os.path.isdir(path) | 判断是否为目录。 |
os.path.isfile(path) | 判断是否为文件。 |
os.path.basename(path) | 返回路径中的最后一部分(文件名)。 |
os.path.dirname(path) | 返回路径中的目录部分。 |
os.path.splitext(path) | 将路径拆分为 (文件名,扩展名),如 ('file', '.txt')。 |
os.path.expanduser(path) | 将 ~ 扩展为当前用户的家目录。 |
示例代码
import os
# 拼接路径
folder = "data"
filename = "input.txt"
file_path = os.path.join(folder, filename)
print(file_path)
# 输出:data/input.txt(Windows 上为 data\input.txt)
# 获取绝对路径
abs_path = os.path.abspath(file_path)
print(abs_path)
# 例如:/home/username/project/data/input.txt
# 检查文件是否存在
if os.path.exists(file_path):
print("文件存在")
else:
print("文件不存在")
# 分离文件名和扩展名
name, ext = os.path.splitext("report.pdf")
print(name) # report
print(ext) # .pdf
# 获取家目录下的文件
home_file = os.path.expanduser("~/documents/notes.txt")
print(home_file)
# /home/username/documents/notes.txt
注意事项
os.path.join 只处理字符串拼接,不会检查路径是否真实存在。从 Python 3.4 开始,官方引入了 pathlib 模块,它将路径表示为对象,提供了更直观、更面向对象的 API。目前 pathlib 已经成为处理路径的首选方式。
核心类:Path
Path 可以代表文件或目录的路径。你可以通过传入字符串创建 Path 对象,然后使用 / 运算符拼接路径,就像在命令行中操作一样。
常用方法
| 方法 / 属性 | 描述 |
|---|---|
Path(path_str) | 创建 Path 对象。 |
/ 运算符 | 拼接路径,例如 Path("data") / "sub" / "file.txt"。 |
Path.cwd() | 获取当前工作目录。 |
Path.home() | 获取当前用户的家目录。 |
path.resolve() | 将相对路径解析为绝对路径,并解析符号链接。 |
path.exists() | 检查路径是否存在。 |
path.is_dir() / is_file() | 判断是目录还是文件。 |
path.name | 获取路径的最后一部分(文件名)。 |
path.stem | 获取文件名(不含扩展名)。 |
path.suffix | 获取文件扩展名。 |
path.parent | 获取父目录路径(可链式调用,如 path.parent.parent)。 |
path.glob(pattern) | 使用通配符匹配目录下的文件,返回生成器。 |
path.iterdir() | 遍历目录下的所有项。 |
path.mkdir(parents=True) | 创建目录,parents=True 可同时创建缺失的父目录。 |
path.read_text(encoding) | 读取文件内容为字符串。 |
path.write_text(data, encoding) | 将字符串写入文件。 |
示例代码
from pathlib import Path
# 创建 Path 对象
base_dir = Path("project")
data_file = base_dir / "data" / "input.txt"
# 使用 / 拼接
print(data_file)
# 输出:project/data/input.txt(自动使用系统分隔符)
# 获取绝对路径
abs_path = data_file.resolve()
print(abs_path)
# 例如:/home/username/project/data/input.txt
# 检查存在性
if data_file.exists():
print("文件存在")
else:
# 创建父目录(如果不存在)
data_file.parent.mkdir(parents=True, exist_ok=True)
# 然后可以创建文件...
# 获取文件信息
print("文件名:", data_file.name) # input.txt
print("文件名 (无扩展):", data_file.stem) # input
print("扩展名:", data_file.suffix) # .txt
print("父目录:", data_file.parent) # project/data
# 遍历目录
for child in Path(".").iterdir():
if child.is_file():
print(f"文件:{child.name}")
elif child.is_dir():
print(f"目录:{child.name}")
# 使用 glob 查找所有 .txt 文件
for txt_file in Path("data").glob("*.txt"):
print(txt_file)
# 读取/写入文件的简洁方式
Path("hello.txt").write_text("Hello, world!", encoding="utf-8")
content = Path("hello.txt").read_text(encoding="utf-8")
print(content)
为什么推荐 pathlib?
/ 运算符直观。os.path 函数。. (当前目录):在相对路径中,./file.txt 和 file.txt 是等价的,通常省略。.. (父目录):用于向上回溯。例如 ../images/logo.png 表示从当前目录的父目录下的 images 文件夹中找 logo.png。~ (用户家目录):在 Unix-like 系统和 Windows 中,~ 都代表当前用户的家目录。在 Windows 上,它通常对应 C:\Users\用户名。Python 的 os.path.expanduser("~/file.txt") 和 pathlib.Path.home() / "file.txt" 能够跨平台正确处理这一扩展,让你轻松访问用户目录下的文件。"C:\Users\name\file.txt" 导致转义问题。folder + "/" + file),容易出错且跨平台兼容性差。os.path.join 或 pathlib。在上一部分中,我们学习了文件路径的基础知识。但有一个关键问题还没有解答:当我们在代码中使用相对路径(如 "data/input.txt")时,Python 到底从哪个目录开始寻找这个文件?答案取决于 当前工作目录。如果不理解这个概念,你的程序很可能在别人的电脑上运行失败,甚至在自己电脑上换个位置运行就会报错。
当前工作目录(Current Working Directory,简称 CWD)是操作系统为每个正在运行的进程维护的一个目录。当你在程序中使用相对路径时,Python 会在这个目录下查找文件。可以把它理解为程序运行时的'家'。
如何获取当前工作目录?
使用 os.getcwd() 或 pathlib.Path.cwd():
import os
from pathlib import Path
print(os.getcwd())
# 例如:C:\Users\Alice\project
print(Path.cwd())
# 同样输出当前工作目录
当前工作目录是如何确定的?
python C:\project\script.py,那么当前工作目录通常是命令行当前所在的目录,而不是脚本所在的目录。这种不确定性正是问题的根源。请看下面的例子:
假设项目结构如下:
C:\project\
│ ├── script.py
└── data\
└── input.txt
script.py 内容:
with open("data/input.txt", "r") as f:
print(f.read())
情况一:在命令行中,先切换到 C:\project 目录,然后运行 python script.py → 成功,因为当前工作目录是 C:\project,相对路径 data/input.txt 可以正确找到。
情况二:在命令行中,当前目录是 C:\,然后运行 python project\script.py → 失败!因为当前工作目录是 C:\,Python 会尝试打开 C:\data\input.txt,但文件实际在 C:\project\data\input.txt,所以抛出 FileNotFoundError。
情况三:在 IDE 中,如果 IDE 将工作目录设置为 C:\project,则成功;如果设置为其他目录,则失败。
由此可见,依赖当前工作目录的相对路径是不稳定的。为了让程序在任何环境下都能正确找到文件,我们需要一种不依赖 CWD 的方法。
__file__ 是一个特殊的 Python 变量,它包含了当前脚本文件的绝对路径(在大多数情况下)。利用这个变量,我们可以获取脚本所在的目录,然后基于这个目录构建相对于脚本的路径。
如何获取脚本所在目录?
import os
from pathlib import Path
# 传统方式
script_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(script_dir, "data", "input.txt")
# 现代方式(pathlib)
script_dir = Path(__file__).resolve().parent
file_path = script_dir / "data" / "input.txt"
关键点:
os.path.abspath(__file__) 或 Path(__file__).resolve() 确保我们得到的是绝对路径,避免 __file__ 在某些情况下是相对路径。os.path.dirname 或 .parent 提取目录部分。现在,无论当前工作目录是什么,file_path 总是指向 script.py 同级的 data/input.txt。这样程序就具备了可移植性——只要整个项目文件夹被完整移动,内部相对关系不变,文件就能被找到。
完整的示例
import os
from pathlib import Path
# 基于 __file__ 构建路径
script_dir = Path(__file__).resolve().parent
data_file = script_dir / "data" / "input.txt"
# 读取文件
try:
with open(data_file, "r", encoding="utf-8") as f:
content = f.read()
print(content)
except FileNotFoundError:
print(f"文件不存在:{data_file}")
注意事项
__file__可能不存在或不可用。这种情况下,你可能需要手动指定路径或使用其他方法。__file__的行为可能会变化,但通常仍有替代方案(如 sys.executable 或 sys.argv[0])。__file__的区别| 特点 | 基于 CWD | 基于 __file__ |
|---|---|---|
| 路径含义 | 相对于运行时的当前目录 | 相对于脚本文件所在目录 |
| 稳定性 | 受运行环境、IDE 设置影响,不稳定 | 只要项目结构不变,路径就固定 |
| 可移植性 | 差,换台电脑可能失效 | 好,整个项目文件夹移动仍可工作 |
| 适用场景 | 临时脚本、与用户交互的文件 | 项目内部资源文件(配置文件、数据等) |
| 代码复杂度 | 简单,直接写相对路径 | 需要几行额外代码,但一劳永逸 |
什么时候可以放心使用 CWD?
~/Documents/),此时使用基于 CWD 或绝对路径是合适的。但作为通用原则,对于项目内部的资源文件(如数据文件、配置文件、日志文件),强烈建议基于 __file__ 构建路径。
pathlib 不仅让路径操作更优雅,还能一步到位完成'获取脚本目录 + 拼接路径'的操作:
from pathlib import Path
# 获取当前脚本所在目录
BASE_DIR = Path(__file__).resolve().parent
# 定义常用目录
DATA_DIR = BASE_DIR / "data"
LOG_DIR = BASE_DIR / "logs"
CONFIG_FILE = BASE_DIR / "config" / "settings.ini"
# 使用
data_file = DATA_DIR / "input.txt"
content = data_file.read_text(encoding="utf-8")
你甚至可以将 BASE_DIR 定义为模块级别的常量,在整个项目中复用。
处理执行文件被打包的情况
如果你用 PyInstaller 打包成单文件可执行程序,运行时 __file__ 可能指向临时解压目录。此时可以用 sys.executable 或 sys.argv[0] 来定位可执行文件的位置,但需要额外处理。不过对于初学者,先掌握常规用法即可。
虽然我们不建议依赖 CWD,但有时确实需要临时改变工作目录。可以使用 os.chdir(path) 或 Path.chdir()。
import os
os.chdir("/path/to/new/dir")
print(os.getcwd()) # 已改变
但是要小心:改变工作目录会影响整个进程,可能导致其他相对路径失效。除非有充分理由,否则应避免。
通过本部分的学习,你应该明白了:
__file__ 可以构建相对于脚本文件的绝对路径,让你的程序具备可移植性。pathlib 来简化路径操作。现在,我们已经解决了'文件在哪里'的问题,可以安全地定位到任何文件。关于文件内容的正式读取与写入操作,将在后续课程中详细展开。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online