Python Pathlib 库实例(批处理图像)
目录一、Pathlib 简介二、核心 API 详解2.1 Path() - 创建路径对象2.2 iterdir() - 遍历目录内容2.3 name 属性 - 获取路径的最后组成部分2.4 exists() - 检查路径是否存在2.5 mkdir() - 创建目录2.6 parent 属性 - 获取父目录2.7 路径拼接/ 运算符2.8 小结整个处理流程中的 Path 对象流转三、完整示例代码四、总结一、Pathlib 简介在 Python 中处理文件路径传统方式通常使用os.path模块虽然功能完整但存在一些不够优雅的地方路径拼接使用字符串操作容易出错跨平台兼容性需要额外处理代码可读性也较差。Python 3.4 引入了pathlib模块提供了面向对象的文件系统路径操作方式。Path类是pathlib的核心它将路径表示为对象而非字符串提供了直观的方法来处理路径、文件和目录。Pathlib 的优势面向对象路径作为对象方法调用更自然跨平台自动处理不同操作系统的路径分隔符链式调用方法可以链式组合代码更简洁功能完整集成了文件系统操作、路径解析、文件读写等丰富功能二、核心 API 详解下面结合我们批量处理图片文件的实例逐一讲解用到的 Path API。我们的程序需要完成以下任务扫描指定目录下所有以sam3_seg_all_为前缀的子目录将每个子目录中的seg_all.jpg文件复制到统一的seg_all目录并按{id}_seg_all.jpg格式重命名。2.1 Path() - 创建路径对象frompathlibimportPath# 从当前文件获取所在目录basePath(__file__).parent# 从字符串创建basePath(/home/user/project)# 从命令行参数创建basePath(sys.argv[1])Path()是pathlib的核心构造函数可以接受字符串、路径片段或特殊变量来创建 Path 对象。__file__是 Python 内置变量表示当前脚本文件的绝对路径。Path(__file__).parent获取脚本所在的目录这比使用os.path.dirname(os.path.abspath(__file__))要简洁得多。创建 Path 对象后我们可以调用各种方法进行路径操作而无需像字符串操作那样担心路径分隔符的问题。在我们的程序中首先通过Path(__file__).parent获取脚本所在目录作为基础目录或者通过sys.argv[1]获取用户指定的目录再使用Path()将其转换为 Path 对象供后续操作使用。2.2 iterdir() - 遍历目录内容defget_dirs(base_dir):return[dfordinbase_dir.iterdir()ifd.is_dir()]iterdir()是 Path 对象的方法返回目录下所有文件和子目录的迭代器。与os.listdir()不同iterdir()返回的是 Path 对象而非字符串这意味着我们无需再拼接路径就能直接对子项进行操作。关键点base_dir是一个 Path 对象代表我们想要遍历的目录循环变量d是 Path 对象代表目录中的每个子项文件或目录d.is_dir()检查当前子项是否为目录返回布尔值结合列表推导式我们一次性筛选出所有子目录对比传统的os.listdir()方式# 传统方式importos dirs[os.path.join(base_dir,d)fordinos.listdir(base_dir)ifos.path.isdir(os.path.join(base_dir,d))]# Pathlib 方式dirs[dfordinbase_dir.iterdir()ifd.is_dir()]Pathlib 方式不仅代码更短而且每个d已经是一个完整的 Path 对象可以直接用于后续操作。2.3 name 属性 - 获取路径的最后组成部分dir_named.name# 获取目录名如 sam3_seg_all_219name属性返回路径的最后一部分相当于os.path.basename()。对于目录路径返回目录名对于文件路径返回文件名。在我们的代码中fordinget_dirs(base_dir):dir_idextract_id(d.name)# d.name 是 sam3_seg_all_219这里的d是 Path 对象代表一个子目录d.name获取这个目录的名称字符串然后传入extract_id()函数提取数字 ID。使用name属性的优势是无需额外调用os.path.basename()直接通过属性访问即可。2.4 exists() - 检查路径是否存在ifnotsrc.exists():print(文件不存在)exists()方法检查 Path 对象所代表的路径在文件系统中是否真实存在。返回True或False。它适用于文件和目录比os.path.exists()更简洁易用。在我们的程序中有两处使用了exists()1. 检查源文件是否存在srcd/seg_all.jpgifnotsrc.exists():results.append((d.name,源文件不存在))continue这里src是 Path 对象代表d目录下的seg_all.jpg文件。调用src.exists()检查该文件是否存在如果不存在则记录错误并跳过。2. 检查目标文件是否已存在ifdst.exists():returnFalse,已存在在copy_file()函数中dst是目标文件路径调用dst.exists()检查是否已经存在同名文件避免重复复制造成覆盖。2.5 mkdir() - 创建目录target.mkdir(exist_okTrue)mkdir()方法用于创建目录参数exist_okTrue表示如果目录已经存在不抛出FileExistsError异常行为类似 Shell 命令中的mkdir -p。在我们的程序中defget_target_dir(base_dir):targetbase_dir/seg_alltarget.mkdir(exist_okTrue)returntarget这里的target是 Path 对象代表base_dir/seg_all路径。调用target.mkdir(exist_okTrue)确保目录存在如果seg_all目录不存在则创建它如果seg_all目录已存在不做任何事这种 “确保目录存在” 的写法非常常用比先判断再创建要简洁得多。如果没有exist_okTrue当目录已存在时会抛出异常需要额外的异常处理。对比传统方式# 传统方式importosifnotos.path.exists(target_path):os.makedirs(target_path)# Pathlib 方式target.mkdir(exist_okTrue)2.6 parent 属性 - 获取父目录parent_dirsrc.parent# 获取文件的父目录parent属性返回路径的父目录相当于os.path.dirname()。可以连续使用多个.parent来获取更上层的目录。在我们的代码中虽然没有直接使用parent但在理解路径结构时很有用。例如srcd/seg_all.jpgparent_dirsrc.parent# 这个 parent_dir 实际上就是 dprint(parent_dird)# True当我们需要获取文件所在目录、或者获取上级目录时parent属性非常方便# 获取上级目录的上级目录grandparentsrc.parent.parent# 获取同目录下的其他文件other_filesrc.parent/other.jpg2.7 路径拼接/ 运算符srcd/seg_all.jpgdsttarget_dir/f{dir_id}_seg_all.jpg/运算符是 Path 最优雅、最核心的特性之一。它用于拼接路径会自动处理不同操作系统的路径分隔符Windows 下是\Linux/macOS 下是/。在我们的程序中/运算符在两处被使用第一处构建源文件路径srcd/seg_all.jpg这里的d是一个 Path 对象代表一个目录例如/home/user/sam3_seg_all_219。通过/运算符拼接seg_all.jpg得到src为Path(/home/user/sam3_seg_all_219/seg_all.jpg)。整个过程是d是 Path 对象通过get_dirs()获得代表sam3_seg_all_219目录d / seg_all.jpg返回新的 Path 对象指向该目录下的seg_all.jpg文件这个拼接操作不会修改原有的d而是创建新的 Path 对象第二处构建目标文件路径dsttarget_dir/f{dir_id}_seg_all.jpg这里的target_dir是 Path 对象代表seg_all目录。dir_id是从目录名中提取的数字 ID如219。f{dir_id}_seg_all.jpg生成字符串219_seg_all.jpg。拼接后的dst为Path(/home/user/seg_all/219_seg_all.jpg)。为什么说 / 运算符优雅传统方式需要这样写# 传统 os.path 方式importos srcos.path.join(d,seg_all.jpg)# 或者使用字符串拼接srcd/seg_all.jpg# 不跨平台Pathlib 的/运算符支持 Path 对象与字符串拼接也支持两个 Path 对象拼接自动处理多余的/不会出现//或路径末尾缺少/的问题跨平台兼容Windows 下自动使用\理解变量类型dPath 对象代表一个子目录srcPath 对象代表源文件路径dstPath 对象代表目标文件路径target_dirPath 对象代表统一存放文件的目录2.8 小结整个处理流程中的 Path 对象流转为了更清晰理解 Path 对象在程序中的流转我们来看看完整的处理流程用户输入基础目录 ↓ base Path(/home/user/project) # Path 对象 ↓ get_dirs(base) ↓ iterdir() 遍历 → [Path(sam3_seg_all_219), Path(sam3_seg_all_220)] ↓ d Path(sam3_seg_all_219) # 每个子目录是 Path 对象 ↓ d.name → sam3_seg_all_219 # 获取目录名字符串 ↓ extract_id() → 219 # 提取 ID ↓ src d / seg_all.jpg # 源文件路径Path(sam3_seg_all_219/seg_all.jpg) ↓ src.exists() 检查文件是否存在 ↓ dst target_dir / 219_seg_all.jpg # 目标文件路径Path(seg_all/219_seg_all.jpg) ↓ shutil.copy2(src, dst) # 执行复制整个过程中Path 对象无缝地从目录遍历、文件名获取、路径拼接、存在性检查到最终的复制操作全程保持类型安全无需进行字符串到路径的转换。三、完整示例代码#!/usr/bin/env python3 批量处理 sam3_seg_all_* 目录中的 seg_all.jpg 文件 将文件重命名为 {id}_seg_all.jpg 并统一收集到 seg_all 目录 importreimportshutilfrompathlibimportPathimportsysdefget_dirs(base_dir): 获取指定目录下的所有子目录 Args: base_dir: Path 对象基础目录 Returns: list: Path 对象列表所有子目录 # iterdir() 返回目录内容迭代器is_dir() 过滤出目录return[dfordinbase_dir.iterdir()ifd.is_dir()]defextract_id(dir_name): 从目录名中提取数字 ID Args: dir_name: 字符串目录名 Returns: str or None: 提取的 ID不匹配则返回 None # re.match 匹配前缀格式group(1) 提取数字部分matchre.match(r^sam3_seg_all_(\d)$,dir_name)returnmatch.group(1)ifmatchelseNonedefget_target_dir(base_dir): 获取目标目录不存在则创建 Args: base_dir: Path 对象基础目录 Returns: Path: 目标目录路径 # 使用 / 操作符拼接路径targetbase_dir/seg_all# mkdir 创建目录exist_okTrue 避免目录已存在时报错target.mkdir(exist_okTrue)returntargetdefcopy_file(src,dst): 复制文件如果目标已存在则跳过 Args: src: Path 对象源文件路径 dst: Path 对象目标文件路径 Returns: tuple: (bool, str) 是否成功及状态信息 # exists() 检查目标文件是否已存在ifdst.exists():returnFalse,已存在# shutil.copy2 复制文件并保留元数据shutil.copy2(src,dst)returnTrue,成功defprocess(base_dir): 主处理函数 Args: base_dir: Path 对象要处理的根目录 Returns: tuple: (list, Path) 处理结果列表和目标目录 # 获取目标目录target_dirget_target_dir(base_dir)results[]# 遍历所有子目录fordinget_dirs(base_dir):# 从目录名提取 IDdir_idextract_id(d.name)# name 属性获取目录名ifnotdir_id:continue# 构建源文件路径使用 / 操作符拼接srcd/seg_all.jpg# exists() 检查源文件是否存在ifnotsrc.exists():results.append((d.name,源文件不存在))continue# 构建目标文件路径使用 f-string 生成新文件名dsttarget_dir/f{dir_id}_seg_all.jpg# 执行复制success,msgcopy_file(src,dst)results.append((d.name,msg))returnresults,target_dirdefmain(): 程序入口 # 获取基础目录命令行参数或脚本所在目录# Path(__file__).parent 获取脚本所在目录basePath(sys.argv[1])iflen(sys.argv)1elsePath(__file__).parentprint(f处理目录:{base})# 执行处理results,targetprocess(base)# 输出结果print(f\n目标目录:{target})forname,msginresults:print(f{name}:{msg})# 统计信息successsum(1for_,msginresultsifmsg成功)skippedsum(1for_,msginresultsifmsg已存在)failedlen(results)-success-skippedprint(f\n总计:{len(results)}个目录)print(f成功:{success}, 跳过:{skipped}, 失败:{failed})if__name____main__:main()四、总结通过这个实际案例我们看到了 Pathlib 如何让文件路径处理变得简洁优雅操作Pathlib 方式传统 os.path 方式获取脚本所在目录Path(__file__).parentos.path.dirname(os.path.abspath(__file__))遍历目录base_dir.iterdir()os.listdir(base_dir)拼接路径dir / file.jpgos.path.join(dir, file.jpg)获取文件名path.nameos.path.basename(path)检查存在性path.exists()os.path.exists(path)创建目录path.mkdir(exist_okTrue)os.makedirs(path, exist_okTrue)获取父目录path.parentos.path.dirname(path)相比传统的os.path方式Pathlib 将路径操作从字符串操作提升为对象操作代码更清晰、更安全、更易维护。推荐使用场景需要大量路径拼接和操作的脚本需要跨平台兼容的项目追求代码可读性和简洁性的开发如果你的 Python 版本 3.4强烈推荐使用 Pathlib 处理文件路径相关任务。对于更复杂的文件系统操作Pathlib 还提供了更多实用的方法如glob()、rglob()、rename()、unlink()等值得深入学习。