pin_drop当前位置:知识文库 ❯ 图文

Python标准库pathlib详解

一、pathlib概述

pathlib 是Python 3.4引入的标准库,提供了一种面向对象的方式来处理文件系统路径。相比于传统的 os.path 模块,pathlib使用 Path 对象来表示路径,使得路径操作更加直观和优雅。

pathlib的核心类是 Path,它有两个主要子类:PurePath(纯路径操作,不访问文件系统)和 ConcretePath(具体路径,可以访问文件系统)。在日常开发中,我们主要使用 Path 类。

代码示例

from pathlib import Path

# 创建Path对象
current_dir = Path('.')
home_dir = Path.home()
desktop = Path.home() / 'Desktop'

print(f"当前目录: {current_dir.absolute()}")
print(f"用户主目录: {home_dir}")

二、Path对象创建与基本操作

1. 创建Path对象

代码示例

from pathlib import Path

# 从字符串创建
p1 = Path('C:/Users/test/file.txt')
p2 = Path('C:', 'Users', 'test', 'file.txt')

# 从变量拼接
folder = 'documents'
filename = 'report.pdf'
p3 = Path('C:/Users/test') / folder / filename

print(p1)
print(p2)
print(p3)

2. 路径属性获取

代码示例

from pathlib import Path

p = Path('C:/Users/test/documents/report.pdf')

print(f"完整路径: {p}")
print(f"驱动器: {p.drive}")           # C:
print(f"根目录: {p.root}")            # /
print(f"锚点: {p.anchor}")            # C:/
print(f"父目录: {p.parent}")          # C:/Users/test/documents
print(f"祖父目录: {p.parent.parent}") # C:/Users/test
print(f"文件名: {p.name}")            # report.pdf
print(f"文件后缀: {p.suffix}")        # .pdf
print(f"文件后缀(无点): {p.suffix[1:]}")  # pdf
print(f"文件名(无后缀): {p.stem}")    # report
print(f"所有后缀: {p.suffixes}")      # ['.pdf']

3. 路径判断与检查

代码示例

from pathlib import Path

# 创建测试文件
test_file = Path('test_example.txt')
test_file.write_text('Hello, pathlib!')

p = Path('test_example.txt')

print(f"是否存在: {p.exists()}")       # True
print(f"是否为文件: {p.is_file()}")    # True
print(f"是否为目录: {p.is_dir()}")     # False
print(f"是否为绝对路径: {p.is_absolute()}")  # False
print(f"文件大小(字节): {p.stat().st_size}")

# 清理测试文件
test_file.unlink()
print(f"删除后是否存在: {test_file.exists()}")  # False

三、路径拼接与解析

1. 使用 / 运算符拼接路径

代码示例

from pathlib import Path

base = Path('C:/projects')

# 使用 / 运算符拼接(推荐方式)
src = base / 'src'
module = src / 'main.py'
config = base / 'config' / 'settings.json'

print(src)      # C:\projects\src
print(module)   # C:\projects\src\main.py
print(config)   # C:\projects\config\settings.json

# 可以连续拼接
full_path = Path('C:') / 'users' / 'test' / 'data.csv'
print(full_path)

2. 路径解析方法

代码示例

from pathlib import Path

# resolve(): 解析为绝对路径,解决 .. 和 .
p = Path('./docs/../src/./main.py')
print(f"原始路径: {p}")
print(f"解析后: {p.resolve()}")

# with_name(): 替换文件名
p = Path('C:/data/report_v1.csv')
new_file = p.with_name('report_v2.csv')
print(new_file)  # C:\data\report_v2.csv

# with_suffix(): 替换文件后缀
py_file = Path('script.py')
txt_file = py_file.with_suffix('.txt')
print(txt_file)  # script.txt

# relative_to(): 计算相对路径
base = Path('C:/projects/myapp')
full = Path('C:/projects/myapp/src/utils/helper.py')
relative = full.relative_to(base)
print(relative)  # src\utils\helper.py

四、文件读写操作

1. 文本文件读写

代码示例

from pathlib import Path

# 创建文件并写入文本
file_path = Path('sample.txt')
file_path.write_text('Hello, pathlib!\n这是第二行\n这是第三行')
print(f"文件已创建: {file_path}")

# 读取全部文本
content = file_path.read_text()
print(f"全部内容:\n{content}")

# 按行读取
lines = file_path.read_text().splitlines()
print(f"行数: {len(lines)}")
for i, line in enumerate(lines, 1):
    print(f"第{i}行: {line}")

# 追加文本
with file_path.open('a', encoding='utf-8') as f:
    f.write('\n这是追加的内容\n')

# 读取字节
# bytes_content = Path('image.png').read_bytes()

2. 使用open()上下文管理器

代码示例

from pathlib import Path
import json

# 写入JSON文件
data = {
    'name': 'Python教程',
    'version': '3.11',
    'topics': ['pathlib', 'os.path', '文件操作']
}

json_path = Path('config.json')
with json_path.open('w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)
print(f"JSON文件已保存: {json_path}")

# 读取JSON文件
with json_path.open('r', encoding='utf-8') as f:
    loaded_data = json.load(f)
print(f"读取的数据: {loaded_data}")

# 清理
json_path.unlink()
Path('sample.txt').unlink()

五、目录遍历与文件匹配

1. 列出目录内容

代码示例

from pathlib import Path

# 创建测试目录结构
test_dir = Path('test_project')
test_dir.mkdir(exist_ok=True)
(test_dir / 'src').mkdir(exist_ok=True)
(test_dir / 'docs').mkdir(exist_ok=True)
(test_dir / 'src' / 'main.py').touch()
(test_dir / 'src' / 'utils.py').touch()
(test_dir / 'docs' / 'readme.md').touch()
(test_dir / 'config.json').touch()

# iterdir(): 列出目录内容
print("目录内容:")
for item in test_dir.iterdir():
    type_str = "目录" if item.is_dir() else "文件"
    print(f"  [{type_str}] {item.name}")

# 列表推导式
files_only = [f.name for f in test_dir.iterdir() if f.is_file()]
dirs_only = [d.name for d in test_dir.iterdir() if d.is_dir()]
print(f"\n文件: {files_only}")
print(f"目录: {dirs_only}")

2. glob模式匹配

代码示例

from pathlib import Path

test_dir = Path('test_project')

# glob(): 查找匹配的文件
print("所有Python文件:")
for f in test_dir.glob('**/*.py'):
    print(f"  {f}")

print("\n所有Markdown文件:")
for f in test_dir.glob('**/*.md'):
    print(f"  {f}")

print("\n所有JSON文件:")
for f in test_dir.glob('**/*.json'):
    print(f"  {f}")

# rglob(): 递归匹配(等价于 **/*)
print("\n递归查找所有.txt文件:")
for f in test_dir.rglob('*.txt'):
    print(f"  {f}")

# 清理测试目录
import shutil
shutil.rmtree(test_dir)

3. walk()递归遍历

代码示例

from pathlib import Path

# Python 3.12+ Path支持walk()方法
# 兼容旧版本的替代方案:

def walk_directory(path):
    """递归遍历目录"""
    p = Path(path)
    for item in sorted(p.rglob('*')):
        indent = '  ' * len(item.relative_to(p).parts) - 1
        prefix = '|-- ' if item.is_file() else '|-- [D] '
        print(f"{indent}{prefix}{item.name}")

# 创建示例结构
demo = Path('demo_folder')
demo.mkdir(exist_ok=True)
(demo / 'level1').mkdir()
(demo / 'level1' / 'level2').mkdir()
(demo / 'file1.txt').touch()
(demo / 'level1' / 'file2.py').touch()
(demo / 'level1' / 'level2' / 'file3.md').touch()

print("目录树结构:")
print(f"[D] {demo.name}")
for item in sorted(demo.rglob('*')):
    depth = len(item.relative_to(demo).parts)
    indent = '    ' * (depth - 1)
    prefix = '|-- '
    if item.is_dir():
        print(f"{indent}{prefix}[D] {item.name}")
    else:
        print(f"{indent}{prefix}{item.name}")

# 清理
import shutil
shutil.rmtree(demo)

六、pathlib与os.path对比

操作 os.path方式 pathlib方式
拼接路径 os.path.join('a', 'b') Path('a') / 'b'
获取绝对路径 os.path.abspath('file') Path('file').resolve()
检查存在 os.path.exists('file') Path('file').exists()
获取父目录 os.path.dirname('a/b/c') Path('a/b/c').parent
获取文件名 os.path.basename('a/b.txt') Path('a/b.txt').name
获取后缀 os.path.splitext('a.txt')[1] Path('a.txt').suffix
读取文件 open('file').read() Path('file').read_text()
创建目录 os.makedirs('a/b/c', exist_ok=True) Path('a/b/c').mkdir(parents=True)
文件匹配 glob.glob('**/*.py', recursive=True) Path('.').glob('**/*.py')

七、注意事项与最佳实践

1. 路径字符串 vs Path对象

注意:虽然Path对象可以自动转换为字符串,但某些旧API(如 open() 在Python 3.5之前)可能不直接接受Path对象。此时需要使用 str(path) 显式转换。Python 3.6+的内置 open() 已经原生支持Path对象。

2. 跨平台兼容性

提示:pathlib会自动处理不同操作系统的路径分隔符。在Windows上使用反斜杠 \,在Linux/macOS上使用正斜杠 /。建议在代码中统一使用正斜杠,pathlib会自动转换。

代码示例

from pathlib import Path

# 推荐使用正斜杠(跨平台兼容)
p = Path('folder/subfolder/file.txt')

# 在Windows上打印时会自动显示为反斜杠
print(p)  # Windows: folder\subfolder\file.txt
          # Linux: folder/subfolder/file.txt

3. 安全删除文件

注意:删除文件前务必检查文件是否存在,避免抛出 FileNotFoundError。可以使用 missing_ok=True 参数(Python 3.8+)来安全删除。

代码示例

from pathlib import Path

# 安全删除文件
file_path = Path('some_file.txt')

# 方法1:先检查再删除
if file_path.exists():
    file_path.unlink()
    print("文件已删除")

# 方法2:使用missing_ok(Python 3.8+)
file_path.unlink(missing_ok=True)
print("安全删除完成(不存在也不报错)")

4. 批量重命名文件

代码示例

from pathlib import Path

# 创建测试文件
demo_dir = Path('rename_demo')
demo_dir.mkdir(exist_ok=True)
for i in range(5):
    (demo_dir / f'photo_{i:02d}.jpg').touch()

# 批量重命名
print("重命名前:")
for f in demo_dir.iterdir():
    print(f"  {f.name}")

for file_path in demo_dir.iterdir():
    if file_path.suffix == '.jpg':
        new_name = file_path.stem.replace('photo', 'image') + '.jpeg'
        new_path = file_path.with_name(new_name).with_suffix('.jpeg')
        file_path.rename(new_path)

print("\n重命名后:")
for f in demo_dir.iterdir():
    print(f"  {f.name}")

# 清理
import shutil
shutil.rmtree(demo_dir)

小贴士

Python官方推荐在新代码中优先使用pathlib而非os.path。pathlib的面向对象设计使代码更简洁易读,特别是在处理复杂路径操作时。如果你的项目需要兼容Python 3.4以下版本,才需要考虑使用os.path。更多关于pathlib的详细信息,可以参考Python官方文档


八、课程小结

  • Path对象:使用Path类表示路径,支持直观的面向对象操作,如属性获取、拼接、解析等

  • 路径拼接:使用 / 运算符替代os.path.join,代码更简洁直观

  • 文件读写:read_text()、write_text()、read_bytes()、write_bytes()方法简化文件I/O操作

  • 目录遍历:iterdir()、glob()、rglob()方法提供强大的目录遍历和文件匹配功能

  • 跨平台兼容:自动处理不同操作系统的路径分隔符,编写一次代码,多处运行

常见问题

pathlib和os.path应该用哪个?

Python官方推荐在新项目中优先使用pathlib。pathlib的面向对象设计使代码更简洁、可读性更好。只有在需要兼容Python 3.4以下版本,或者与只接受字符串路径的第三方库交互时,才考虑使用os.path。

Path对象可以直接传给open()函数吗?

在Python 3.6+中,内置的open()函数原生支持Path对象,可以直接传入。如果你使用的是Python 3.5或更早版本,则需要使用str(path)将Path对象转换为字符串。

glob()和rglob()有什么区别?

glob()只匹配当前目录或指定模式的文件,需要使用 **/* 前缀才能递归;rglob()本身就是递归匹配,等价于glob('**/pattern')。例如 Path('.').rglob('*.py') 等价于 Path('.').glob('**/*.py')。

如何判断两个Path对象是否指向同一个文件?

可以使用 resolve() 方法将两个路径都解析为绝对路径后再比较。例如:path1.resolve() == path2.resolve()。resolve() 会解析符号链接、去除 . 和 .. 等相对路径元素,确保比较的是实际的文件位置。

练习1

使用pathlib编写程序,扫描指定目录,统计各种文件类型(按后缀分类)的数量,并输出一个类似"图片文件(.jpg, .png): 15个"、"文档文件(.pdf, .docx): 8个"的分类统计报告。

练习2

编写一个函数,接收一个目录路径作为参数,使用pathlib递归查找该目录下所有超过指定大小(如1MB)的文件,并按照文件大小从大到小排序输出。该工具可用于查找磁盘中的大文件。

标签: pathlib Path对象 Python标准库 文件操作 路径操作 Python入门

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python标准库functools详解 下一篇: Python文件打开与关闭 - open函数和close方法

poll相关推荐