跳到主要内容

pathlib之面向对象的文件系统路径

Reference:

模块概述

应用场景:

如果你厌倦了使用os包提供的接口去操作文件路径……

如果你更喜欢以面向对象的方式去操作文件路径……

那么,pathlib 也许是一个不错的选择!!!

模块介绍

pathlib(Object-oriented filesystem paths)是python官方封装提供的一种使用面向对象的方式操作文件路径的工具包,在python3.4中发布,可以支持跨平台(Unix和Windows),pathlib基于是否有I/O操作提供了两种方式来操作文件路径,一种是提供纯计算操作而没有I/O的纯路径,另一种是提供I/O操作的具体路径

         +----------+
| |
---------| PurePath |--------
| | | |
| +----------+ |
| | |
| | |
v | v
+---------------+ | +-----------------+
| | | | |
| PurePosixPath | | | PureWindowsPath |
| | | | |
+---------------+ | +-----------------+
| v |
| +------+ |
| | | |
| -------| Path |------ |
| | | | | |
| | +------+ | |
| | | |
| | | |
v v v v
+-----------+ +-------------+
| | | |
| PosixPath | | WindowsPath |
| | | |
+-----------+ +-------------+

实现原理:

pathlib包主要组成结构:

  • PurePath(object): 该类提供一套基于非I/O操作的文件路径的接口。

  • PurePosixPath(PurePath): 该提供一套在Unix环境中基于非I/O操作的文件路径接口。

  • PureWindowsPath(PurePath): 该提供一套在Windows环境中基于非I/O操作的文件路径接口。

  • Path(PurePath): 该类提供一套基于I/O操作的文件路径接口。

  • PosixPath(Path, PurePosixPath): 该类提供一套在Unix环境中基于I/O操作的文件路径接口。

  • WindowsPath(Path, PureWindowsPath): 该类提供一套在windows环境中基于I/O操作的文件路径接口。

通常,Path是在日常开发中使用频率最高的类,可以类比os.path。

Path类的常用接口

备注:下列仅列出了日常开发中常用的接口,更全的接口清单请见官方文档

路径(查找)接口:

  • Path.cwd():

    • 返回一个新的表示当前目录的路径对象(和 os.getcwd() 返回的相同)
  • Path.home():

    • 返回一个表示用户家目录的新路径对象(与带 ~ 构造的 os.path.expanduser() 所返回的相同)。 如果无法解析家目录,则会引发 RuntimeError
  • Path.exists(*, follow_symlinks=True):

    • 如果路径指向现有的文件或目录则返回 True
  • Path.glob(pattern, *, case_sensitive=None):

    • 解析相对于此路径的通配符 pattern,产生所有匹配的文件,pattern 的形式与 fnmatch --- Unix 文件名模式匹配 的相同,还增加了 "**" 表示 "此目录以及所有子目录,递归"。 换句话说,它启用递归通配
    • 在一个较大的目录树中使用 "**" 模式可能会消耗非常多的时间。
  • Path.rglob(pattern, *, case_sensitive=None):

    • 递归地对给定的相对 pattern 执行 glob 通配。 这类似于当调用 Path.glob() 时在 pattern 之前加上 "**/",其中 patternsfnmatch 中的相同:
  • Path.iterdir():

    • 当路径指向一个目录时,产生该路径下的对象的路径, 子条目会以任意顺序生成,并且不包括特殊条目 '.' 和 '..'。 如果迭代器创建之后有文件在目录中被移除或添加,是否要包括该文件所对应的路径对象并没有明确规定。
  • Path.walk(top_down=True, on_error=None, follow_symlinks=False)

    • 通过对目录树自上而下或自下而上的遍历来生成其中的文件名。对于根位置为 self 的目录树中的每个目录(包括 self 但不包括 '.' 和 '..'),该方法会产生一个 3 元组 (dirpath, dirnames, filenames)。
      • dirpath_ 是指向当前正被遍历到的目录的 Path
      • dirnames_ 是由表示 dirpath 中子目录名称的字符串组成的列表 (不包括 '.' 和 '..'),
      • filenames_ 是由表示 dirpath 中非目录文件名称的字符串组成的列表。 要获取 dirpath 中文件或目录的完整路径 (以 self 开头),可使用 dirpath / name。 这些列表是否排序取决于具体的文件系统。
  • Path.absolute():

    • 改为绝对路径
  • Path.resolve(strict=False):

    • 将路径绝对化

路径(判断)接口

  • Path.is_dir():

    • 如果路径指向一个目录(或者一个指向目录的符号链接)则返回 True,如果指向其他类型的文件则返回 False。
  • Path.is_file():

    • 如果路径指向一个正常的文件(或者一个指向正常文件的符号链接)则返回 True,如果指向其他类型的文件则返回 False。
  • Path.samefile(other_path):

路径(读写)接口

  • Path.mkdir(mode=0o777, parents=False, exist_ok=False):

    • 新建给定路径的目录。如果给出了 mode ,它将与当前进程的 umask 值合并来决定文件模式和访问标志。如果路径已经存在,则抛出 FileExistsError
    • parents ->True: 任何找不到的父目录都会伴随着此路径被创建;它们会以默认权限被创建,而不考虑 mode 设置
    • parents ->False,则找不到的父级目录会引发 FileNotFoundError
    • exist_ok 为 false(默认),则在目标已存在的情况下抛出 FileExistsError.
    • exist_ok 为 true, 则 FileExistsError 异常将被忽略(和 POSIX mkdir -p 命令行为相同),但是只有在最后一个路径组件不是现存的非目录文件时才生效。
  • Path.rmdir():

    • 移除此目录。此目录必须为空的。
  • Path.touch(mode=0o666, exist_ok=True)

    • 将给定的路径创建为文件。如果给出了 mode 它将与当前进程的 umask 值合并以确定文件的模式和访问标志。如果文件已经存在,则当 exist_ok 为 true 则函数仍会成功(并且将它的修改事件更新为当前事件),否则抛出 FileExistsError
  • Path.rename(target):

    • 将文件名目录重命名为给定的 target,并返回一个新的指向 target 的 Path 实例。 在 Unix 上,如果 target 存在且为一个文件,如果用户有足够权限则它将被静默地替换。 在 Windows 上,如果 target 存在,则将会引发 FileExistsErrortarget 可以是一个字符串或者另一个路径对象:
  • Path.open(mode='r', buffering=- 1, encoding=None, errors=None, newline=None):

    • 打开路径指向的文件,就像内置的 open() 函数所做的一样
  • Path.read_bytes():

    • 以字节对象的形式返回路径指向的文件的二进制内容
  • Path.read_text(encoding=None, errors=None):

    • 以字符串形式返回路径指向的文件的解码后文本内容。
  • Path.write_bytes(data):

    • 将文件以二进制模式打开,写入 data 并关闭,一个同名的现存文件将被覆盖。
  • Path.write_text(data, encoding=None, errors=None, newline=None):

    • 将文件以文本模式打开,写入 data 并关闭,同名的现有文件会被覆盖。 可选形参的含义与 open() 的相同。
  • Path.stat(*, follow_symlinks=True):

    • 返回一个 os.stat_result 对象,其中包含有关此路径的信息,例如 os.stat()。 结果会在每次调用此方法时重新搜索。

路径(权限)接口

  • Path.chmod(mode, *, follow_symlinks=True)

    • 改变文件模式和权限,和 os.chmod() 一样。
  • Path.owner():

    • 返回拥有此文件的用户名。如果文件的 UID 无法在系统数据库中找到,则抛出 KeyError
  • Path.group():

    • 返回拥有此文件的用户组。如果文件的 GID 无法在系统数据库中找到,将抛出 KeyError

pathlib和os接口对照

pathlibosos.path
Path.absolute() 1改为绝对路径,返回新的路径对象os.path.abspath()
Path.resolve()将路径绝对化,返回新的路径对象os.path.realpath()
Path.chmod()改变文件模式和权限os.chmod()
Path.mkdir()新建给定的路径os.mkdir()
Path.mkdir()同上os.makedirs()
Path.rename()将文件名目录重命名为给定的 target,需要权限os.rename()
Path.replace()将文件或目录重命名为给定的 target,强制替换os.replace()
Path.rmdir()移除一个空目录os.rmdir()
Path.unlink()移除一个文件os.remove(), os.unlink()
Path.cwd()放回当前路径的路径对象os.getcwd()
Path.exists()判断文件或目录是否存在os.path.exists()
Path.expanduser()Path.home()获取表示用户家目录的新路径对象os.path.expanduser()
Path.iterdir()遍历当前路径os.listdir()
Path.walk()通过对目录树自上而下或自下而上的遍历来生成其中的文件名os.walk()
Path.is_dir()是否是目录os.path.isdir()
Path.is_file()是否文件os.path.isfile()
Path.is_symlink()-os.path.islink()
Path.hardlink_to()-os.link()
Path.symlink_to()-os.symlink()
Path.readlink()-os.readlink()
PurePath.relative_to() 2-os.path.relpath()
Path.stat(), Path.owner(), Path.group()获取文件和路径信息、权限os.stat()
PurePath.is_absolute()是否是觉得路径os.path.isabs()
PurePath.joinpath()路径拼接os.path.join()
PurePath.name获取文件或目录名os.path.basename()
PurePath.parent获取上级目录os.path.dirname()
Path.samefile()判断是相同文件os.path.samefile()
PurePath.stemPurePath.suffix获取文件名称,获取文件后缀os.path.splitext()

实践应用

前置说明:方便起见,导包路径不在下列案例中重复添加

from pathlib import *

import os

获取文件的父级目录

# pathlib写法
pathlib.Path.cwd().parent

# os写法
os.path.dirname(os.path.dirname(os.getcwd()))

路径拼接

# pathlib写法
paths = ('ssw', 'test')
pathlib.Path.cwd().parent.joinpath(*paths)
# 或
pathlib.Path.cwd().parent / 'ssw' / 'test'
# 或
pathlib.Path.cwd().parent / 'ssw/test'

# os写法
os.path.join(os.path.dirname(os.getcwd()), 'ssw', 'test')

判断文件类别

In [5]: path_tmp = Path('/data/sswang/projects/test/opserver/manage.py')

In [6]: Path(path_tmp).parent
Out[6]: PosixPath('/data/sswang/projects/test/opserver')

In [7]: Path(path_tmp).parents
Out[7]: <PosixPath.parents>

In [10]: list(Path(path_tmp).parents)
Out[10]:
[PosixPath('/data/sswang/projects/test/opserver'),
PosixPath('/data/sswang/projects/test'),
PosixPath('/data/sswang/projects'),
PosixPath('/data/sswang'),
PosixPath('/data'),
PosixPath('/')]

In [11]: Path(path_tmp).name
Out[11]: 'manage.py'

In [12]: Path(path_tmp).stem
Out[12]: 'manage'

In [13]: Path(path_tmp).suffix
Out[13]: '.py'

In [14]: Path(path_tmp).suffixes
Out[14]: ['.py']

打开文件

In [39]: with p.open() as f:
...: f.readline()
...:

In [40]: p.read_text()
Out[40]: '#!/usr/bin/env python\n"""Django\'s command-line utility for administrative tasks."""\nimport os\nimport sys\n\n\ndef main():\n """Run administrative tasks."""\n os.environ.setdefault(\'DJANGO_SETTINGS_MODULE\', \'main.settings\')\n try:\n from django.core.management import execute_from_command_line\n except ImportError as exc:\n raise ImportError(\n "Couldn\'t import Django. Are you sure it\'s installed and "\n "available on your PYTHONPATH environment variable? Did you "\n "forget to activate a virtual environment?"\n ) from exc\n execute_from_command_line(sys.argv)\n\n\nif __name__ == \'__main__\':\n main()\n'

获取文件信息

In [1]: from pathlib import *

In [2]: p1= Path.cwd()

In [3]: p1
Out[3]: PosixPath('/data/sswang/projects/test/opserver')

In [6]: p2 = Path('/data/sswang/projects/test/opserver/manage.py')

In [7]: p2.stat()
Out[7]: os.stat_result(st_mode=33188, st_ino=219153651, st_dev=2081, st_nlink=1, st_uid=0, st_gid=0, st_size=682, st_atime=1619528866, st_mtime=1619528322, st_ctime=1619528322)

In [9]: p2.stat().st_size
Out[9]: 682

In [10]: p2.stat().st_atime
Out[10]: 1619528866.8391194

In [11]: p2.stat().st_mode
Out[11]: 33188

查找文件

In [12]: p3= Path('.').glob("*.py")

In [13]: p
Out[13]: <generator object Path.glob at 0x7f7baf232810>

In [15]: p= Path('.').glob("*.py")

In [16]: list(p)
Out[16]:
[PosixPath('config_local.py'),
PosixPath('config_public.py'),
PosixPath('config.py'),
PosixPath('manage.py')]

In [17]: p1= Path('.').glob("**/*.py")

In [18]: list(p1)
Out[18]:
[PosixPath('config_local.py'),
PosixPath('config_public.py'),
PosixPath('config.py'),
PosixPath('manage.py'),
PosixPath('opserver/apps.py'),
......
]