pathlib.Path
pathlib 是 Python 标准库中的路径处理模块。
导入:
from pathlib import Path
传统路径经常使用字符串:
file_path = "data/stocks/600519.json"
使用 Path:
file_path = Path("data/stocks/600519.json")
Path 不只是保存路径字符串,还提供了大量文件和目录操作方法:
file_path.exists()
file_path.is_file()
file_path.read_text()
file_path.write_text()
file_path.rename()
file_path.unlink()
使用 Path 的优点:
- 路径拼接更清晰
- 自动适配 Windows、macOS 和 Linux
- 文件读写更方便
- 目录遍历更直观
- 减少手动处理路径分隔符
- 代码可读性更好
from pathlib import Path
file_path = Path("data.txt")
目录路径:
folder_path = Path("data")
多级路径:
file_path = Path(
"data/stocks/600519.json"
)
Path 可以接收多个路径部分:
file_path = Path(
"data",
"stocks",
"600519.json",
)
等价于:
file_path = Path(
"data/stocks/600519.json"
)
在 macOS 或 Linux:
print(type(Path("data.txt")))
结果通常是:
<class 'pathlib.PosixPath'>
在 Windows:
<class 'pathlib.WindowsPath'>
平时只需要使用:
Path
不需要手动区分操作系统。
Path 重载了 / 运算符,可以用来拼接路径。
folder = Path("data")
file_path = folder / "stocks.json"
print(file_path)
结果:
data/stocks.json
多级拼接:
file_path = (
Path("data")
/ "stocks"
/ "daily"
/ "600519.json"
)
推荐:
file_path = base_dir / "data" / "stocks.json"
不推荐手动拼接字符串:
file_path = (
str(base_dir)
+ "/data/stocks.json"
)
手动使用 / 或 \,容易出现跨平台问题。
current_dir = Path.cwd()
print(current_dir)
当前工作目录是程序运行时所在的目录。
它不一定是 Python 文件所在目录。
例如:
cd /Users/martin
python projects/demo/main.py
此时:
Path.cwd()
通常是:
/Users/martin
而不是:
/Users/martin/projects/demo
home_dir = Path.home()
print(home_dir)
macOS 可能是:
/Users/martin
Windows 可能是:
C:\Users\martin
下面不会自动展开 ~:
path = Path("~/Documents")
应使用:
path = Path(
"~/Documents"
).expanduser()
结果类似:
/Users/martin/Documents
在普通 Python 文件中,可以使用:
BASE_DIR = Path(
__file__
).resolve().parent
含义:
__file__ 当前 Python 文件路径
resolve() 转换为规范化绝对路径
parent 获取所在目录
项目示例:
project/
├── data/
│ └── stocks.json
└── main.py
main.py:
from pathlib import Path
BASE_DIR = Path(
__file__
).resolve().parent
DATA_FILE = (
BASE_DIR
/ "data"
/ "stocks.json"
)
print(DATA_FILE)
这种方式不受当前工作目录影响。
注意,在某些交互式环境中,可能不存在:
__file__
例如:
- Python 交互解释器
- 部分 Notebook 环境
这些环境通常使用:
Path.cwd()
path = Path(
"/Users/martin/data.txt"
)
print(path.is_absolute())
Windows 示例:
path = Path(
r"C:\Users\martin\data.txt"
)
path = Path("data.txt")
print(path.absolute())
absolute() 将相对路径转换成绝对路径。
但它不会完整解析:
..- 符号链接
- 路径规范化问题
path = Path(
"data/../data/stocks.json"
)
resolved_path = path.resolve()
print(resolved_path)
resolve() 会:
- 转为绝对路径
- 处理
.和.. - 尽可能解析符号链接
默认:
path.resolve(strict=False)
即使目标不存在,也通常可以得到规范化路径。
要求路径必须存在:
path.resolve(strict=True)
不存在时会抛出:
FileNotFoundError
file_path = Path(
"data/stocks/600519.daily.json"
)
print(file_path.name)
结果:
600519.daily.json
print(file_path.stem)
结果:
600519.daily
stem 只移除最后一个扩展名。
print(file_path.suffix)
结果:
.json
print(file_path.suffixes)
结果:
[
".daily",
".json",
]
另一个例子:
path = Path("archive.tar.gz")
print(path.stem)
print(path.suffix)
print(path.suffixes)
结果:
archive.tar
.gz
['.tar', '.gz']
print(file_path.parent)
结果:
data/stocks
继续向上:
print(file_path.parent.parent)
结果:
data
for parent in file_path.parents:
print(parent)
print(file_path.parts)
结果类似:
(
"data",
"stocks",
"600519.daily.json",
)
这些方法只生成新路径,不会立即修改磁盘文件。
path = Path(
"data/stocks.txt"
)
new_path = path.with_name(
"new_stocks.txt"
)
print(new_path)
结果:
data/new_stocks.txt
new_path = path.with_stem(
"stock_list"
)
结果:
data/stock_list.txt
new_path = path.with_suffix(
".csv"
)
结果:
data/stocks.csv
去掉扩展名:
new_path = path.with_suffix("")
source = Path(
"data/stocks.json"
)
backup = source.with_name(
f"{source.stem}_backup"
f"{source.suffix}"
)
print(backup)
结果:
data/stocks_backup.json
path = Path("data.txt")
if path.exists():
print("路径存在")
if path.is_file():
print("这是文件")
folder = Path("data")
if folder.is_dir():
print("这是目录")
if path.is_symlink():
print("这是符号链接")
if path.is_mount():
print("这是挂载点")
path = Path(
"/Users/martin/project/data/a.txt"
)
base = Path(
"/Users/martin/project"
)
print(
path.is_relative_to(base)
)
结果:
True
folder = Path("data")
folder.mkdir()
如果目录已经存在,会抛出:
FileExistsError
folder.mkdir(
exist_ok=True,
)
folder = Path(
"data/stocks/daily"
)
folder.mkdir(
parents=True,
exist_ok=True,
)
参数含义:
parents=True
自动创建缺失的父目录
exist_ok=True
目录已经存在时不报错
项目中最常见的写法:
output_dir.mkdir(
parents=True,
exist_ok=True,
)
file_path = Path("data.txt")
file_path.touch()
文件不存在时,会创建空文件。
文件已存在时,会更新文件时间。
防止覆盖或触碰已有文件:
file_path.touch(
exist_ok=False,
)
文件存在时会抛出:
FileExistsError
file_path = Path("data.txt")
content = file_path.read_text(
encoding="utf-8",
)
print(content)
返回值类型:
str
适合较小文本文件。
with file_path.open(
"r",
encoding="utf-8",
) as file:
content = file.read()
适合:
- 按行读取
- 大文件处理
- 需要控制打开参数
- 需要文件对象
with file_path.open(
"r",
encoding="utf-8",
) as file:
for line in file:
print(
line.rstrip("\n")
)
获取行号:
with file_path.open(
"r",
encoding="utf-8",
) as file:
for line_number, line in enumerate(
file,
start=1,
):
print(
line_number,
line.rstrip("\n"),
)
file_path = Path("output.txt")
count = file_path.write_text(
"Hello Python",
encoding="utf-8",
)
print(count)
返回写入字符数量。
注意,write_text() 会覆盖原文件。
lines = [
"第一行",
"第二行",
"第三行",
]
content = "\n".join(lines) + "\n"
file_path.write_text(
content,
encoding="utf-8",
)
Path.write_text() 不支持追加模式。
追加应使用:
with file_path.open(
"a",
encoding="utf-8",
) as file:
file.write("新增内容\n")
image_path = Path("image.jpg")
data = image_path.read_bytes()
print(type(data))
结果:
<class 'bytes'>
target_path = Path(
"image_copy.jpg"
)
target_path.write_bytes(data)
适合较小二进制文件。
大文件应使用分块读取。
def read_chunks(
file_path: Path,
*,
chunk_size: int = 1024 * 1024,
):
with file_path.open(
"rb"
) as file:
while chunk := file.read(
chunk_size
):
yield chunk
使用:
for chunk in read_chunks(
Path("large_file.bin")
):
print(len(chunk))
folder = Path("data")
for path in folder.iterdir():
print(path)
只遍历当前目录中的直接子项。
不会递归进入子目录。
区分文件和目录:
for path in folder.iterdir():
if path.is_file():
print("文件:", path)
elif path.is_dir():
print("目录:", path)
查找当前目录中的 TXT 文件:
for file_path in folder.glob(
"*.txt"
):
print(file_path)
匹配 JSON:
folder.glob("*.json")
匹配固定开头:
folder.glob(
"stock_*.csv"
)
递归查找:
for file_path in folder.rglob(
"*.json"
):
print(file_path)
会搜索当前目录及所有子目录。
for file_path in folder.glob(
"**/*.json"
):
print(file_path)
通常下面更清晰:
folder.rglob("*.json")
Python 3.12 为 Path 增加了 walk() 方法。
folder = Path("data")
for root, directories, files in (
folder.walk()
):
print("当前目录:", root)
print("子目录:", directories)
print("文件:", files)
其中:
root 当前 Path 目录
directories 当前目录下的子目录名称列表
files 当前目录下的文件名称列表
注意,directories 和 files 中通常是名称字符串,不是完整 Path。
组合完整路径:
for root, directories, files in (
folder.walk()
):
for file_name in files:
file_path = (
root
/ file_name
)
print(file_path)
默认:
folder.walk(
top_down=True
)
先处理父目录,再处理子目录。
for root, directories, files in (
folder.walk(
top_down=False
)
):
print(root)
先处理最深层子目录。
删除目录树时,自下而上更有用。
for root, directories, files in (
folder.walk()
):
directories[:] = [
name
for name in directories
if name not in {
".git",
"__pycache__",
".venv",
}
]
for file_name in files:
print(root / file_name)
必须使用:
directories[:]
原地修改列表,才能影响后续递归。
files = sorted(
Path("data").glob("*.txt")
)
按文件名:
files = sorted(
Path("data").glob("*.txt"),
key=lambda path: path.name,
)
忽略大小写:
files = sorted(
Path("data").glob("*"),
key=lambda path: (
path.name.lower()
),
)
按文件大小:
files = sorted(
Path("data").glob("*"),
key=lambda path: (
path.stat().st_size
),
)
按修改时间:
files = sorted(
Path("data").glob("*"),
key=lambda path: (
path.stat().st_mtime
),
reverse=True,
)
file_path = Path("data.txt")
info = file_path.stat()
print(info.st_size)
单位是字节。
print(info.st_mtime)
这是时间戳。
转换为日期时间:
from datetime import datetime
modified_time = datetime.fromtimestamp(
info.st_mtime
)
print(modified_time)
print(info.st_atime)
print(info.st_ctime)
注意,st_ctime 在不同操作系统上的含义可能不同:
- Windows:通常接近创建时间
- Unix/macOS:通常表示元数据状态变化时间
不能统一把它理解为创建时间。
def format_size(
size: int,
) -> str:
units = [
"B",
"KB",
"MB",
"GB",
"TB",
]
value = float(size)
for unit in units:
if value < 1024:
return (
f"{value:.2f} {unit}"
)
value /= 1024
return f"{value:.2f} PB"
使用:
size = file_path.stat().st_size
print(format_size(size))
base = Path(
"/Users/martin/project"
)
file_path = Path(
"/Users/martin/project/data/a.txt"
)
relative_path = file_path.relative_to(
base
)
print(relative_path)
结果:
data/a.txt
如果目标不在 base 内,会抛出:
ValueError
安全使用:
if file_path.is_relative_to(base):
relative_path = (
file_path.relative_to(base)
)
path1 = Path(
"data/stocks.json"
)
path2 = Path(
"data/stocks.json"
)
print(path1 == path2)
结果:
True
这比较的是路径表示。
判断两个路径是否指向同一个实际文件:
path1 = Path("data.txt")
path2 = Path("./data.txt")
print(
path1.samefile(path2)
)
路径必须存在。
samefile() 可以识别:
- 不同相对路径
- 符号链接
- 硬链接
- 同一实际文件
old_path = Path("old.txt")
new_path = Path("new.txt")
result_path = old_path.rename(
new_path
)
print(result_path)
可以同时移动到另一个目录:
source = Path("data.txt")
target = Path(
"archive/data.txt"
)
target.parent.mkdir(
parents=True,
exist_ok=True,
)
source.rename(target)
不同文件系统之间移动时,rename() 可能失败。
复杂移动建议使用:
shutil.move()
source = Path("new.txt")
target = Path("old.txt")
source.replace(target)
replace() 通常允许覆盖已经存在的目标文件。
适合:
- 临时文件替换正式文件
- 安全写入后的最终替换
- 明确允许覆盖目标的场景
Python 3.12 中,复制和复杂移动通常配合 shutil。
from pathlib import Path
import shutil
source = Path("data.txt")
target = Path(
"backup/data.txt"
)
target.parent.mkdir(
parents=True,
exist_ok=True,
)
shutil.copy2(
source,
target,
)
copy2() 会尽可能保留文件元数据。
shutil.move(
source,
target,
)
shutil.copytree(
Path("source_folder"),
Path("target_folder"),
)
目标目录允许已经存在:
shutil.copytree(
source,
target,
dirs_exist_ok=True,
)
file_path = Path("data.txt")
file_path.unlink()
文件不存在时:
FileNotFoundError
忽略不存在:
file_path.unlink(
missing_ok=True
)
folder = Path("empty_folder")
folder.rmdir()
目录必须为空。
import shutil
shutil.rmtree(
Path("output")
)
这是破坏性操作,会递归删除所有内容。
建议加入严格检查:
def safe_remove_tree(
folder: Path,
) -> None:
resolved = folder.resolve()
forbidden = {
Path.home().resolve(),
Path("/").resolve(),
}
if resolved in forbidden:
raise ValueError(
f"禁止删除危险目录:"
f"{resolved}"
)
if resolved.exists():
shutil.rmtree(resolved)
target = Path("data.txt")
link = Path("data_link.txt")
link.symlink_to(target)
目录符号链接:
link.symlink_to(
target_folder,
target_is_directory=True,
)
Windows 创建符号链接可能需要额外权限。
print(link.is_symlink())
print(link.readlink())
source = Path("data.txt")
hard_link = Path(
"data_hard_link.txt"
)
hard_link.hardlink_to(source)
硬链接和原文件指向相同底层文件数据。
删除其中一个名称,不一定会删除实际数据,只要仍有其他硬链接存在。
path = Path("script.sh")
path.chmod(0o755)
在 Unix/macOS 中:
755
所有者可读、写、执行
其他用户可读、执行
Windows 权限模型不同,行为可能有限。
在支持的平台上:
print(path.owner())
获取所属组:
print(path.group())
这些方法在不同操作系统上的支持情况可能不同。
path = Path("data.txt")
text = str(path)
部分旧库只接受字符串路径:
legacy_function(
str(path)
)
不过现代 Python 大多数文件相关函数都支持 Path。
import os
path_text = os.fspath(path)
它返回系统可接受的路径表示。
推荐:
path = (
Path("data")
/ "stocks.json"
)
path.read_text(
encoding="utf-8"
)
不推荐:
path = str(
Path("data")
/ "stocks.json"
)
过早转字符串会失去 Path 的便利方法。
Windows 路径包含反斜杠:
C:\Users\martin\data.txt
普通字符串中,反斜杠可能是转义符。
例如:
path = "C:\new\test.txt"
其中:
\n
\t
可能被解释为换行和制表符。
可以使用原始字符串:
path = Path(
r"C:\new\test.txt"
)
或者使用正斜杠:
path = Path(
"C:/new/test.txt"
)
更推荐分段:
path = Path(
"C:/",
"new",
"test.txt",
)
Path 会访问实际文件系统。
如果只想处理路径字符串,而不访问磁盘,可以使用:
from pathlib import PurePath
path = PurePath(
"data/stocks.json"
)
print(path.name)
print(path.stem)
print(path.suffix)
print(path.parent)
专门处理 Windows 路径:
from pathlib import PureWindowsPath
path = PureWindowsPath(
r"C:\Users\martin\data.txt"
)
专门处理 POSIX 路径:
from pathlib import PurePosixPath
path = PurePosixPath(
"/home/martin/data.txt"
)
适用场景:
- 跨平台路径字符串转换
- 不依赖当前操作系统
- 处理远程系统路径
- 只进行路径计算
普通文件操作直接使用:
Path
from pathlib import Path
import json
读取:
def read_json(
file_path: Path,
) -> object:
text = file_path.read_text(
encoding="utf-8"
)
return json.loads(text)
写入:
def write_json(
file_path: Path,
data: object,
) -> None:
file_path.parent.mkdir(
parents=True,
exist_ok=True,
)
text = json.dumps(
data,
ensure_ascii=False,
indent=2,
)
file_path.write_text(
text,
encoding="utf-8",
)
使用:
stocks = [
{
"code": "600519",
"name": "贵州茅台",
},
{
"code": "000001",
"name": "平安银行",
},
]
write_json(
Path("data/stocks.json"),
stocks,
)
from pathlib import Path
import csv
读取:
def read_csv(
file_path: Path,
) -> list[dict[str, str]]:
rows: list[
dict[str, str]
] = []
with file_path.open(
"r",
encoding="utf-8",
newline="",
) as file:
reader = csv.DictReader(file)
for row in reader:
rows.append(dict(row))
return rows
写入:
def write_csv(
file_path: Path,
rows: list[
dict[str, object]
],
field_names: list[str],
) -> None:
file_path.parent.mkdir(
parents=True,
exist_ok=True,
)
with file_path.open(
"w",
encoding="utf-8-sig",
newline="",
) as file:
writer = csv.DictWriter(
file,
fieldnames=field_names,
)
writer.writeheader()
writer.writerows(rows)
例如,将 .jpeg 改为 .jpg:
folder = Path("images")
for file_path in folder.glob(
"*.jpeg"
):
new_path = (
file_path.with_suffix(
".jpg"
)
)
file_path.rename(new_path)
for file_path in folder.iterdir():
if not file_path.is_file():
continue
new_path = file_path.with_name(
f"backup_{file_path.name}"
)
file_path.rename(new_path)
files = sorted(
folder.glob("*.jpg")
)
for index, file_path in enumerate(
files,
start=1,
):
new_name = (
f"image_{index:03d}"
f"{file_path.suffix.lower()}"
)
new_path = (
file_path.with_name(
new_name
)
)
print(
file_path,
"→",
new_path,
)
实际修改:
file_path.rename(new_path)
批量重命名建议先预览,不要立即修改。
def get_unique_path(
target: Path,
) -> Path:
if not target.exists():
return target
index = 1
while True:
candidate = target.with_name(
f"{target.stem}_{index}"
f"{target.suffix}"
)
if not candidate.exists():
return candidate
index += 1
使用:
target = get_unique_path(
Path("output/data.txt")
)
print(target)
可能得到:
output/data_1.txt
按扩展名分类文件:
from pathlib import Path
import shutil
CATEGORIES = {
"images": {
".jpg",
".jpeg",
".png",
".webp",
".gif",
},
"documents": {
".txt",
".pdf",
".docx",
".xlsx",
},
"audio": {
".mp3",
".wav",
".flac",
".m4a",
},
"video": {
".mp4",
".mov",
".mkv",
},
}
def get_category(
file_path: Path,
) -> str:
suffix = (
file_path.suffix.lower()
)
for category, suffixes in (
CATEGORIES.items()
):
if suffix in suffixes:
return category
return "others"
def organize_folder(
folder: Path,
*,
dry_run: bool = True,
) -> None:
if not folder.is_dir():
raise NotADirectoryError(
f"目录不存在:{folder}"
)
for file_path in list(
folder.iterdir()
):
if not file_path.is_file():
continue
category = get_category(
file_path
)
target_dir = (
folder
/ category
)
target_path = (
target_dir
/ file_path.name
)
target_path = get_unique_path(
target_path
)
print(
f"{file_path.name} "
f"→ {target_path}"
)
if dry_run:
continue
target_dir.mkdir(
parents=True,
exist_ok=True,
)
shutil.move(
file_path,
target_path,
)
预览:
organize_folder(
Path("downloads"),
dry_run=True,
)
实际执行:
organize_folder(
Path("downloads"),
dry_run=False,
)
def search_text_files(
folder: Path,
keyword: str,
*,
pattern: str = "*.txt",
) -> list[
tuple[Path, int, str]
]:
results: list[
tuple[Path, int, str]
] = []
for file_path in folder.rglob(
pattern
):
try:
with file_path.open(
"r",
encoding="utf-8",
) as file:
for line_number, line in (
enumerate(
file,
start=1,
)
):
if keyword in line:
results.append(
(
file_path,
line_number,
line.rstrip(
"\n"
),
)
)
except UnicodeDecodeError:
continue
return results
使用:
results = search_text_files(
Path("notes"),
"股票",
)
for path, line_number, line in results:
print(
path,
line_number,
line,
)
def find_latest_file(
folder: Path,
pattern: str = "*",
) -> Path | None:
files = [
path
for path in folder.glob(
pattern
)
if path.is_file()
]
if not files:
return None
return max(
files,
key=lambda path: (
path.stat().st_mtime
),
)
使用:
latest_file = find_latest_file(
Path("data"),
"*.xlsx",
)
print(latest_file)
def calculate_directory_size(
folder: Path,
) -> int:
total = 0
for path in folder.rglob("*"):
if not path.is_file():
continue
try:
total += (
path.stat().st_size
)
except OSError:
continue
return total
使用:
size = calculate_directory_size(
Path("data")
)
print(format_size(size))
重要文件可以先写临时文件,再替换正式文件。
from pathlib import Path
import os
import tempfile
def safe_write_text(
file_path: Path,
content: str,
*,
encoding: str = "utf-8",
) -> None:
file_path.parent.mkdir(
parents=True,
exist_ok=True,
)
with tempfile.NamedTemporaryFile(
mode="w",
encoding=encoding,
dir=file_path.parent,
delete=False,
) as temp_file:
temp_file.write(content)
temp_path = Path(
temp_file.name
)
try:
os.replace(
temp_path,
file_path,
)
except Exception:
temp_path.unlink(
missing_ok=True,
)
raise
适合:
- 配置文件
- JSON 数据
- 交易记录
- 重要计算结果
- 不能接受部分写入的文件
常见文件异常:
FileNotFoundError
FileExistsError
PermissionError
IsADirectoryError
NotADirectoryError
UnicodeDecodeError
OSError
示例:
def read_text_file(
file_path: Path,
) -> str:
try:
return file_path.read_text(
encoding="utf-8"
)
except FileNotFoundError as error:
raise FileNotFoundError(
f"文件不存在:"
f"{file_path}"
) from error
except IsADirectoryError as error:
raise ValueError(
f"路径是目录:"
f"{file_path}"
) from error
except PermissionError as error:
raise PermissionError(
f"无权读取文件:"
f"{file_path}"
) from error
except UnicodeDecodeError as error:
raise ValueError(
f"文件不是 UTF-8 编码:"
f"{file_path}"
) from error
Path("data/stocks.json")
通常相对于:
Path.cwd()
不是一定相对于脚本文件。
需要稳定项目路径时:
BASE_DIR = Path(
__file__
).resolve().parent
错误:
path = Path("data")
path = path + "/stocks.json"
正确:
path = (
Path("data")
/ "stocks.json"
)
Path("data").read_text()
会抛出:
IsADirectoryError
可以先判断:
if path.is_file():
content = path.read_text(
encoding="utf-8"
)
path.write_text(
"新内容",
encoding="utf-8",
)
会覆盖原内容。
追加内容应使用:
with path.open(
"a",
encoding="utf-8",
) as file:
file.write("追加内容")
危险:
for path in folder.rglob("*"):
path.unlink()
建议先输出:
for path in folder.rglob("*"):
print("计划删除:", path)
并使用:
dry_run=True
只判断:
path.suffix == ".jpg"
无法匹配:
.JPG
.Jpg
推荐:
path.suffix.lower() == ".jpg"
if path.exists():
不能说明它是文件还是目录。
根据需求使用:
path.is_file()
path.is_dir()
path1.samefile(path2)
要求路径实际存在。
否则可能抛出:
FileNotFoundError
创建路径:
path = Path("data.txt")
拼接路径:
path = (
Path("data")
/ "stocks.json"
)
当前目录:
Path.cwd()
用户目录:
Path.home()
脚本目录:
Path(__file__).resolve().parent
展开用户目录:
Path("~/data").expanduser()
规范化绝对路径:
path.resolve()
路径信息:
path.name
path.stem
path.suffix
path.suffixes
path.parent
path.parents
path.parts
判断:
path.exists()
path.is_file()
path.is_dir()
path.is_symlink()
path.is_absolute()
path.is_relative_to(base)
创建:
path.mkdir(
parents=True,
exist_ok=True,
)
path.touch()
读取:
path.read_text(
encoding="utf-8"
)
path.read_bytes()
写入:
path.write_text(
content,
encoding="utf-8"
)
path.write_bytes(data)
遍历:
folder.iterdir()
folder.glob("*.txt")
folder.rglob("*.txt")
folder.walk()
修改路径名称:
path.with_name("new.txt")
path.with_stem("new")
path.with_suffix(".json")
实际重命名:
path.rename(new_path)
替换:
path.replace(target)
删除文件:
path.unlink(
missing_ok=True
)
删除空目录:
path.rmdir()
获取信息:
path.stat()
相对路径:
path.relative_to(base)
比较实际文件:
path1.samefile(path2)
创建目录:
data/stocks/daily
参考答案:
Path(
"data/stocks/daily"
).mkdir(
parents=True,
exist_ok=True,
)
查找目录中所有 Excel 文件。
参考答案:
files = [
path
for path in Path(
"data"
).rglob("*")
if (
path.is_file()
and path.suffix.lower()
in {
".xlsx",
".xls",
}
)
]
获取文件名、主名和扩展名。
path = Path(
"data/report.xlsx"
)
print(path.name)
print(path.stem)
print(path.suffix)
把所有 .txt 文件复制到备份目录。
import shutil
source_dir = Path("data")
backup_dir = Path("backup")
backup_dir.mkdir(
parents=True,
exist_ok=True,
)
for file_path in source_dir.rglob(
"*.txt"
):
relative_path = (
file_path.relative_to(
source_dir
)
)
target = (
backup_dir
/ relative_path
)
target.parent.mkdir(
parents=True,
exist_ok=True,
)
shutil.copy2(
file_path,
target,
)
找出目录中大于 10 MB 的文件。
limit = 10 * 1024 * 1024
large_files = [
path
for path in Path(
"data"
).rglob("*")
if (
path.is_file()
and path.stat().st_size
> limit
)
]
将文件扩展名统一转为小写。
for file_path in Path(
"data"
).iterdir():
if not file_path.is_file():
continue
suffix = (
file_path.suffix
)
if not suffix:
continue
lower_suffix = suffix.lower()
if suffix == lower_suffix:
continue
new_path = (
file_path.with_suffix(
lower_suffix
)
)
print(
file_path,
"→",
new_path,
)
确认无冲突后执行:
file_path.rename(new_path)
pathlib.Path 的本质是:
用对象表示路径
+
用对象方法操作文件系统
最常用的写法:
from pathlib import Path
BASE_DIR = Path(
__file__
).resolve().parent
data_file = (
BASE_DIR
/ "data"
/ "stocks.json"
)
小文件读写:
text = data_file.read_text(
encoding="utf-8"
)
data_file.write_text(
text,
encoding="utf-8"
)
目录遍历:
for file_path in Path(
"data"
).rglob("*.json"):
print(file_path)
目录创建:
output_dir.mkdir(
parents=True,
exist_ok=True,
)
实际项目中应重点记住:
路径拼接使用 /
文本读写明确编码
项目路径不要依赖 cwd
大文件使用 open 逐行处理
复制移动配合 shutil
破坏性操作先预览
Python 3.12 可以使用 Path.walk
掌握 Path 后,文件操作代码会比传统字符串路径和 os.path 写法更加清晰、稳定和易维护。