☰
Python Pathlib 库实例(批处理图像)
2026/10/27 5:14:29 网站建设 项目流程

📑 目录

  • 一、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# 从当前文件获取所在目录base=Path(__file__).parent# 从字符串创建base=Path("/home/user/project")# 从命令行参数创建base=Path(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_name=d.name# 获取目录名,如 "sam3_seg_all_219"

name属性返回路径的最后一部分,相当于os.path.basename()。对于目录路径,返回目录名;对于文件路径,返回文件名。

在我们的代码中:

fordinget_dirs(base_dir):dir_id=extract_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. 检查源文件是否存在:

src=d/"seg_all.jpg"ifnotsrc.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_ok=True)

mkdir()方法用于创建目录,参数exist_ok=True表示如果目录已经存在,不抛出FileExistsError异常,行为类似 Shell 命令中的mkdir -p。

在我们的程序中:

defget_target_dir(base_dir):target=base_dir/"seg_all"target.mkdir(exist_ok=True)returntarget

这里的target是 Path 对象,代表base_dir/seg_all路径。调用target.mkdir(exist_ok=True)确保目录存在:

  • 如果seg_all目录不存在,则创建它
  • 如果seg_all目录已存在,不做任何事

这种 “确保目录存在” 的写法非常常用,比先判断再创建要简洁得多。如果没有exist_ok=True,当目录已存在时会抛出异常,需要额外的异常处理。

对比传统方式:

# 传统方式importosifnotos.path.exists(target_path):os.makedirs(target_path)# Pathlib 方式target.mkdir(exist_ok=True)

2.6 parent 属性 - 获取父目录

parent_dir=src.parent# 获取文件的父目录

parent属性返回路径的父目录,相当于os.path.dirname()。可以连续使用多个.parent来获取更上层的目录。

在我们的代码中虽然没有直接使用parent,但在理解路径结构时很有用。例如:

src=d/"seg_all.jpg"parent_dir=src.parent# 这个 parent_dir 实际上就是 dprint(parent_dir==d)# True

当我们需要获取文件所在目录、或者获取上级目录时,parent属性非常方便:

# 获取上级目录的上级目录grandparent=src.parent.parent# 获取同目录下的其他文件other_file=src.parent/"other.jpg"

2.7 路径拼接(/ 运算符)

src=d/"seg_all.jpg"dst=target_dir/f"{dir_id}_seg_all.jpg"

/运算符是 Path 最优雅、最核心的特性之一。它用于拼接路径,会自动处理不同操作系统的路径分隔符(Windows 下是\,Linux/macOS 下是/)。

在我们的程序中,/运算符在两处被使用:

第一处:构建源文件路径

src=d/"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 对象

第二处:构建目标文件路径

dst=target_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 src=os.path.join(d,"seg_all.jpg")# 或者使用字符串拼接src=d+"/"+"seg_all.jpg"# 不跨平台

Pathlib 的/运算符:

  • 支持 Path 对象与字符串拼接
  • 也支持两个 Path 对象拼接
  • 自动处理多余的/,不会出现//或路径末尾缺少/的问题
  • 跨平台兼容,Windows 下自动使用\

理解变量类型:

  • d:Path 对象,代表一个子目录
  • src:Path 对象,代表源文件路径
  • dst:Path 对象,代表目标文件路径
  • target_dir:Path 对象,代表统一存放文件的目录

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) 提取数字部分match=re.match(r'^sam3_seg_all_(\d+)$',dir_name)returnmatch.group(1)ifmatchelseNonedefget_target_dir(base_dir):""" 获取目标目录,不存在则创建 Args: base_dir: Path 对象,基础目录 Returns: Path: 目标目录路径 """# 使用 / 操作符拼接路径target=base_dir/"seg_all"# mkdir 创建目录,exist_ok=True 避免目录已存在时报错target.mkdir(exist_ok=True)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_dir=get_target_dir(base_dir)results=[]# 遍历所有子目录fordinget_dirs(base_dir):# 从目录名提取 IDdir_id=extract_id(d.name)# name 属性获取目录名ifnotdir_id:continue# 构建源文件路径,使用 / 操作符拼接src=d/"seg_all.jpg"# exists() 检查源文件是否存在ifnotsrc.exists():results.append((d.name,"源文件不存在"))continue# 构建目标文件路径,使用 f-string 生成新文件名dst=target_dir/f"{dir_id}_seg_all.jpg"# 执行复制success,msg=copy_file(src,dst)results.append((d.name,msg))returnresults,target_dirdefmain():""" 程序入口 """# 获取基础目录:命令行参数或脚本所在目录# Path(__file__).parent 获取脚本所在目录base=Path(sys.argv[1])iflen(sys.argv)>1elsePath(__file__).parentprint(f"处理目录:{base}")# 执行处理results,target=process(base)# 输出结果print(f"\n目标目录:{target}")forname,msginresults:print(f"{name}:{msg}")# 统计信息success=sum(1for_,msginresultsifmsg=="成功")skipped=sum(1for_,msginresultsifmsg=="已存在")failed=len(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.jpg"os.path.join(dir, "file.jpg")
获取文件名path.nameos.path.basename(path)
检查存在性path.exists()os.path.exists(path)
创建目录path.mkdir(exist_ok=True)os.makedirs(path, exist_ok=True)
获取父目录path.parentos.path.dirname(path)

相比传统的os.path方式,Pathlib 将路径操作从字符串操作提升为对象操作,代码更清晰、更安全、更易维护。

推荐使用场景:

  • 需要大量路径拼接和操作的脚本
  • 需要跨平台兼容的项目
  • 追求代码可读性和简洁性的开发

如果你的 Python 版本 >= 3.4,强烈推荐使用 Pathlib 处理文件路径相关任务。对于更复杂的文件系统操作,Pathlib 还提供了更多实用的方法,如glob()、rglob()、rename()、unlink()等,值得深入学习。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询