from pathlib import Path
for item in Path("data").iterdir():
print(item)
特性:
不进入子目录
适合结构已知、层级固定的场景
不会产生递归风险
2. 递归遍历:遍历整个目录树
for file in Path("data").rglob("*"):
print(file)
特性:
自动递归所有子目录
适合未知结构或全量处理
必须配合过滤规则使用
(三)os.walk:工程级目录遍历接口
尽管 pathlib 更优雅,但在工程中,os.walk 仍然是最可控的遍历工具。
import os
for root, dirs, files in os.walk("data"):
print(root)
print(dirs)
print(files)
返回值语义必须非常清楚:
root:当前目录路径
dirs:当前目录下的子目录名列表
files:当前目录下的文件名列表
(四)遍历顺序与可控性
os.walk 默认是自顶向下(top-down)遍历。
os.walk("data", topdown=True)
这意味着:
父目录先于子目录被处理
可以在遍历过程中动态修改 dirs 来剪枝
示例:跳过隐藏目录
for root, dirs, files in os.walk("data"):
dirs[:] = [d for d in dirs ifnot d.startswith(".")]
这是 os.walk 的工程级优势,pathlib 无法直接做到。
(五)在遍历中执行规则化操作
遍历本身毫无意义,规则才是价值所在。
示例:只处理 .log 文件
from pathlib import Path
import os
for root, _, files in os.walk("logs"):
for name in files:
path = Path(root) / name
if path.suffix == ".log":
print("process:", path)
规则应当满足:
明确(可读)
可组合
不依赖外部状态
(六)遍历中的'危险操作'防护
在遍历中执行删除、移动等操作时,必须格外谨慎。
1. 不要在遍历同一目录时修改结构
错误示例:
for root, dirs, files in os.walk("data"):
for f in files:
os.remove(Path(root) / f)
问题:
可能影响后续遍历
在某些系统上行为不可预测
2. 推荐模式:先收集,再操作
from pathlib import Path
import os
to_delete = []
for root, _, files in os.walk("data"):
for name in files:
path = Path(root) / name
if path.suffix == ".tmp":
to_delete.append(path)
for path in to_delete:
path.unlink()
这是工程中最安全的遍历模型。
(七)遍历性能与范围控制
递归遍历的成本与目录规模成正比,必须主动限制范围。
示例:限制最大深度
from pathlib import Path
base = Path("data").resolve()
for path in base.rglob("*"):
iflen(path.relative_to(base).parts) > 3:
continueprint(path)
(八)目录遍历的通用抽象模式
总结一个通用、可复用的遍历模板:
from pathlib import Path
import os
defwalk_files(root: Path, predicate):
for current, _, files in os.walk(root):
for name in files:
path = Path(current) / name
if predicate(path):
yield path
使用:
logs = walk_files(
Path("logs"),
lambda p: p.suffix == ".log"and p.stat().st_size > 0
)
for log in logs:
print(log)
import zipfile
from pathlib import Path
base_dir = Path("release")
zip_path = Path("release.zip")
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for file in base_dir.rglob("*"):
if file.is_file():
zf.write(file, file.relative_to(base_dir))
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for file in base_dir.rglob("*"):
if should_include(file):
zf.write(file, file.relative_to(base_dir))
这是工程中最基本的质量控制手段。
(六)ZIP 内容检查与读取
ZIP 不只是'写完就算',很多场景需要读取和校验。
1. 列出归档内容
with zipfile.ZipFile("release.zip") as zf:
for name in zf.namelist():
print(name)
这是验证归档结构是否正确的第一步。
2. 读取单个文件内容
with zipfile.ZipFile("release.zip") as zf:
with zf.open("conf/app.yaml") as f:
content = f.read().decode("utf-8")
注意:
返回的是类文件对象
内容默认是字节流
(七)解压 ZIP:功能与风险并存
1. 基本解压
with zipfile.ZipFile("release.zip") as zf:
zf.extractall("output")
这是最简单、也是最危险的用法。
2. 路径穿越风险(必须理解)
恶意 ZIP 可能包含如下路径:
../../etc/passwd
直接解压将覆盖系统文件。
3. 安全解压示例(工程必备)
from pathlib import Path
import zipfile
defsafe_extract(zip_path: Path, target_dir: Path):
target_dir = target_dir.resolve()
with zipfile.ZipFile(zip_path) as zf:
for member in zf.namelist():
dest = (target_dir / member).resolve()
ifnotstr(dest).startswith(str(target_dir)):
raise RuntimeError(f"unsafe path: {member}")
zf.extractall(target_dir)
import os
import shutil
for root, _, files in os.walk(INPUT_DIR):
for name in files:
src = Path(root) / name
rel = classify(src)
if rel isNone:
continue
dst = RELEASE_DIR / rel
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
import zipfile
with zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED) as zf:
for file in RELEASE_DIR.rglob("*"):
if file.is_file():
zf.write(file, file.relative_to(RELEASE_DIR))
要点:
永远使用相对路径
发布目录即归档根
将流程封装为可复用函数
defbuild_release(input_dir: Path, output_dir: Path, zip_path: Path):
# 初始化if output_dir.exists():
shutil.rmtree(output_dir)
output_dir.mkdir()
# 组织文件for root, _, files in os.walk(input_dir):
for name in files:
src = Path(root) / name
rel = classify(src)
if rel isNone:
continue
dst = output_dir / rel
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
# 归档with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for file in output_dir.rglob("*"):
if file.is_file():
zf.write(file, file.relative_to(output_dir))
这一步的意义在于:
流程具备函数边界
易于测试、复用、集成到 CI
(三)组合能力才是工程能力
我们完成了从'工具'到'系统'的转变:
单点 API 不构成工程能力
明确阶段与边界,流程自然稳定
文件组织的本质是规则驱动的数据流
到这里,你已经具备了:
设计完整文件组织流程的能力
将目录、遍历、复制、归档组合为稳定系统的经验
最后我们将从工程经验出发,总结常见错误、风险点与最佳实践,帮助你避免'能跑但不可靠'的实现。
八、常见错误与工程级注意事项
(一)路径拼接错误:字符串是隐患源头
错误示例:
path = "data/" + filename
问题:
分隔符被硬编码
无法处理复杂路径
跨平台不可靠
正确做法:
from pathlib import Path
path = Path("data") / filename