1. 文件路径是什么? #
- 项目里经常要拼配置文件路径、日志目录、上传保存位置、导出文件目录。
- 不应在代码里硬编码
C:\foo\bar 或与 ./data/file.txt 混用不同风格的分隔符。
- Windows 用
\,Linux/macOS 用 /,手写字符串拼接在部署到服务器时容易出错。
- 新代码推荐
pathlib.Path:用 / 拼接路径、判断存在、建目录、glob 查找,跨平台由库处理。
- 老项目、第三方库仍常见
os.path,能读懂即可。
2. 核心概念 #
- 理解下面三个概念,才能正确选相对路径还是绝对路径。
- 也能明白为何要用库拼接,而不是手写字符串加反斜杠或斜杠。
| 概念 |
说明 |
| 绝对路径 |
从盘符/根目录开始的完整路径 |
| 相对路径 |
相对当前工作目录 |
| 跨平台 |
Windows 用 \,Linux/macOS 用 /;用 Path 或 os.path.join 自动处理 |
- 当前工作目录(cwd) 会随「从哪执行脚本」变化。
- 业务路径宜相对项目根或配置里的基准目录,而不要假设 cwd 固定。
3. pathlib.Path(推荐) #
pathlib.Path 是 Python 3.4+ 标准库提供的面向对象路径 API。
- 拼接用
Path("a") / "b",判断用 exists()、is_file(),建目录用 mkdir()。
- 查找用
glob/rglob,还可 read_text/write_text 读写小文件。
- 下面分创建拼接、属性检查、建目录与查找三部分。
3.1 创建与拼接 #
Path(字符串) 创建路径对象;/ 运算符拼接子路径。
resolve() 转为绝对路径并解析 .. 等。
Path(__file__).resolve().parent 常用来定位「当前脚本所在目录」。
- 再相对项目根拼
config/、logs/ 等——Django 的 BASE_DIR 即同类思路。
from pathlib import Path
p = Path("data") / "logs" / "app.log"
abs_p = p.resolve()
base = Path(__file__).resolve().parent
config = base / "config" / "settings.json"
项目里常用「以项目根为基准」:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
env_file = BASE_DIR / ".env"
print(env_file)
3.2 常用属性与检查 #
- 路径对象可拆出文件名
name、不含后缀的 stem、后缀 suffix、父目录 parent。
- 便于日志命名、按扩展名过滤。
- 操作磁盘前用
exists()、is_file()、is_dir() 判断,避免直接 open 不存在的路径。
from pathlib import Path
f = Path("/var/log/app.log")
print(f.name)
print(f.stem)
print(f.suffix)
print(f.parent)
print(f.exists())
print(f.is_file())
print(f.is_dir())
3.3 建目录、找文件 #
- 写入日志、上传文件前,目标目录可能尚不存在。
mkdir(parents=True, exist_ok=True) 可创建多级目录,已存在不报错。
glob("*.py") 匹配当前目录;rglob("*.py") 递归子目录。
- 适合收集导出文件、批量处理脚本。
from pathlib import Path
log_dir = Path("logs") / "2025"
log_dir.mkdir(parents=True, exist_ok=True)
for py in Path(".").glob("*.py"):
print(py)
for py in Path("src").rglob("*.py"):
print(py)
| 方法 |
作用 |
mkdir(parents=True, exist_ok=True) |
创建目录(含父级) |
glob("*.json") |
当前目录通配 |
rglob("*.py") |
递归子目录通配 |
4. os.path(读老代码时) #
os.path 是较早的跨平台路径模块。
os.path.join 拼接;exists/isfile/isdir 判断;basename/dirname 拆分。
- 维护老项目、读 Stack Overflow 旧帖时会遇到。
- 新代码优先
Path;同一逻辑不要 os.path.join 与 Path / 混用导致重复拼接。
import os
full = os.path.join("data", "uploads", "a.png")
print(os.path.exists(full))
print(os.path.isfile(full))
print(os.path.isdir("data"))
print(os.path.basename(full))
print(os.path.dirname(full))
5. 与读写配合 #
Path 可直接 read_text(encoding="utf-8") / write_text(...) 读写小文本。
- 也可把
Path 对象传给 open(),与文件操作章节的 with open 写法完全兼容。
- 大文件仍建议
open + 逐行读,不必 read_text 一次载入。
path = Path("config.json")
text = path.read_text(encoding="utf-8")
path.write_text('{"ok": true}', encoding="utf-8")
with open(path, "r", encoding="utf-8") as f:
data = f.read()
6. 项目中的典型用法 #
- 实际项目里路径通常围绕三件事:定项目根(BASE_DIR)、启动前确保目录存在、按模式收集一批文件。
- Web 框架的
MEDIA_ROOT、STATIC_ROOT 等多由 settings 配置,本质仍是路径字符串或 Path。
- 代码里用相对项目根拼接,不要写死本机绝对路径(如
D:\project\...)。
- 否则换机器或上 Linux 部署容易找不到文件。
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_DIR = BASE_DIR / "templates"
(BASE_DIR / "media" / "uploads").mkdir(parents=True, exist_ok=True)
csv_files = list((BASE_DIR / "exports").glob("*.csv"))
7. 项目开发要点 #
- 路径错误会导致「本地能跑、服务器找不到文件」或安全漏洞。
- 新代码用
pathlib.Path,拼接用 /,避免字符串手动加 \\。
- 路径放配置(环境变量、
.env、Django settings),代码里用 BASE_DIR / "..." 相对项目根。
- 写文件前先
mkdir(parents=True, exist_ok=True),避免目录不存在报错。
- 操作前
exists() / is_file(),并处理 FileNotFoundError。
- 用户传入的路径要校验,防止
../../../etc/passwd 类路径遍历(上传、下载接口)。
- 不要依赖「当前工作目录」;脚本从哪里执行 cwd 会变,用
__file__ 或配置里的绝对基准。
8. 总结 #
- 路径处理记住:
Path + 项目根基准 + 配置化。
- 写前建目录、读前判断存在;用户传入路径须防遍历。
- 下表按需求速查。
| 需求 |
推荐写法 |
| 拼接 |
Path("a") / "b" / "c.txt" |
| 项目根 |
Path(__file__).resolve().parent |
| 是否存在 |
path.exists()、path.is_file() |
| 建目录 |
path.mkdir(parents=True, exist_ok=True) |
| 找文件 |
path.glob(...)、path.rglob(...) |
| 老项目 |
os.path.join、os.path.exists |