目的: 使用面向对象的 API 解析, 构建, 测试和处理文件名和路径, 而不是底层的字符串操作.
Path 表示
pathlib 包含用于管理使用 POSIX 标准或 Microsoft Windows 语法格式化的文件系统路径的类. 它包括所谓的 "pure" 类, 它们对字符串进行操作, 但不与实际的文件系统进行交互;"concrete" 类将 API 扩展为包含反映或修改本地文件系统数据的操作.
pure 类 PurePosixPath 和 PureWindowsPath 可以在任何操作系统上实例化和使用, 因为它们只用于名称. 要实例化正确的类来处理真实的文件系统, 请使用 Path 来获取 PosixPath 或 WindowsPath, 具体取决于平台.
构建 Path
要实例化路径, 字符串作为参数. Path 对象的字符串表示是该字符串. 要创建引用相对于现有路径的值的新路径, 请使用 / 运算符来扩展路径. 运算符的参数可以是字符串或其他路径对象.
- import pathlib
- usr = pathlib.PurePosixPath('/usr')
- print(usr)
- usr_local = usr / 'local'
- print(usr_local)
- usr_share = usr / pathlib.PurePosixPath('share')
- print(usr_share)
- root = usr / '..'
- print(root)
- etc = root / '/etc/'
- print(etc)
执行结果
- /usr
- /usr/local
- /usr/share
- /usr/..
- /etc
正如示例输出中的 root 值所示, 操作符按照给定的方式组合路径值, 并且在包含父目录引用 ".." 的情况下不会对结果进行解析. 但是, 如果以 os.sep 开头它被解释为新的 "根" 引用.
具体路径类的 resolve()方法, 通过查看目录和符号链接的文件系统来解析路径, 并生成名称引用的绝对路径.
- # 讨论 钉钉免费群 21745728 qq 群 144081101 567351477
- # pathlib_resolve.py
- import pathlib
- usr_local = pathlib.Path('/usr/local')
- share = usr_local / '..' / 'share'
- print(type(share.resolve()))
- print(share.resolve())
执行结果:
- <class 'pathlib.PosixPath'>
- /usr/share
joinpath()可以组合路径.
- # pathlib_joinpath.py
- import pathlib
- root = pathlib.PurePosixPath('/')
- subdirs = ['usr', 'local']
- usr_local = root.joinpath(*subdirs)
- print(usr_local)
执行结果:
- $ python3 pathlib_joinpath.py
- /usr/local
使用 with_name()创建新路径, 用不同的文件名替换路径的名称部分. 使用 Use with_suffix()创建新路径, 用不同的值替换文件名的扩展名.
- # pathlib_from_existing.py
- import pathlib
- ind = pathlib.PurePosixPath('source/pathlib/index.rst')
- print(ind)
- py = ind.with_name('pathlib_from_existing.py')
- print(py)
- pyc = py.with_suffix('.pyc')
- print(pyc)
执行结果:
- $ python3 pathlib_from_existing.py
- source/pathlib/index.rst
- source/pathlib/pathlib_from_existing.py
- source/pathlib/pathlib_from_existing.pyc
解析路径
Path 对象具有用于从名称中提取部分值的方法和属性. 例如, parts 属性会生成一系列基于路径分隔符值解析的路径段.
- # pathlib_parts.py
- import pathlib
- p = pathlib.PurePosixPath('/usr/local')
- print(p.parts)
执行结果:
- $ python3 pathlib_parts.py
- ('/', 'usr', 'local')
有两种方法可以从给定的路径对象中 "向上" 导航文件系统层次结构. 父属性引用包含路径的目录的新路径实例, 即 os.path.dirname()返回的值. 父项属性是迭代器, 它产生父目录引用, 不断地向上 "提升" 路径层次直到到达根目录.
- import pathlib
- p = pathlib.PurePosixPath('/usr/local/lib')
- print('parent: {}'.format(p.parent))
- print('\nhierarchy:')
- for up in p.parents:
- print(up)
执行结果:
- $ python3 pathlib_parents.py
- parent: /usr/local
- hierarchy:
- /usr/local
- /usr
- /
路径的其他部分可以通过路径对象的属性来访问. name 属性保存最后一个路径分隔符 (与 os.path.basename() 产生的值相同)后的最后一部分路径. 后缀属性保存扩展分隔符后面的值, 并且 stem 属性保留后缀之前的名称部分.
- #pathlib_name.py import pathlib p = pathlib.PurePosixPath('./source/pathlib/pathlib_name.py') print('path : {}'.format(p)) print('name : {}'.format(p.name)) print('suffix: {}'.format(p.suffix)) print('stem : {}'.format(p.stem))
执行结果:
- $ python3 pathlib_name.py
- path : source/pathlib/pathlib_name.py
- name : pathlib_name.py
- suffix: .py
- stem : pathlib_name
尽管 suffix 和 stem 与 os.path.splitext()生成的值相似, 但值仅基于 name 而不是完整路径.
创建 Concrete 路径
Concrete Path 类的实例可以通过引用文件系统上的文件, 目录或符号链接的名称 (或潜在名称) 的字符串参数来创建. 该类还提供了几种便捷方法来构建使用常用位置 (如当前工作目录和用户主目录) 的实例.
- # pathlib_convenience.py
- import pathlib
- home = pathlib.Path.home()
- print('home:', home)
- cwd = pathlib.Path.cwd()
- print('cwd :', cwd)
执行结果:
- home: /home/andrew
- cwd : /home/andrew/code/python-chinese-library/libraries/pathlib
- stem : pathlib_name
目录内容
有三种方法可以访问目录列表, 以发现文件系统上可用文件的名称. iterdir()是生成器, 为包含目录中的每个项目生成新的 Path 实例.
- # pathlib_convenience.py
- import pathlib
- home = pathlib.Path.home()
- print('home:', home)
- cwd = pathlib.Path.cwd()
- print('cwd :', cwd)
执行结果:
pathlib_name.py
pathlib_parents.py
pathlib_parts.py
pathlib_joinpath.py
pathlib_from_existing.py
pathlib_operator.py
pathlib_resolve.py
pathlib_convenience.py
pathlib_iterdir.py
如果路径不是目录, 则 iterdir()会引发 NotADirectoryError.
使用 glob()仅查找匹配模式的文件.
- import pathlib
- p = pathlib.Path('.')
- for f in p.glob('*.py'):
- print(f)
执行结果:
pathlib_glob.py
pathlib_name.py
pathlib_parents.py
pathlib_parts.py
pathlib_glob.py
pathlib_joinpath.py
pathlib_from_existing.py
pathlib_operator.py
pathlib_resolve.py
pathlib_convenience.py
pathlib_iterdir.py
glob 处理器支持使用模式前缀 ** 或通过调用 rglob()而不是 glob()来进行递归扫描.
- # pathlib_rglob.py
- import pathlib
- p = pathlib.Path('..')
- for f in p.rglob('*.py'):
- print(f)
执行结果:
- ../heapq/heapq_demo.py
- ../_dubbo/dubbo.py
- ../pathlib/pathlib_name.py
- ../pathlib/pathlib_parents.py
- ../pathlib/pathlib_rglob.py
- ../pathlib/pathlib_parts.py
- ...
下面的两种用法也实现了类似的功能:
- # pathlib_rglob2.py
- import pathlib
- p = pathlib.Path('..')
- for f in p.rglob('**/*.py'):
- print(f)
- # pathlib_rglob3.py
- import pathlib
- p = pathlib.Path('..')
- for f in p.glob('**/*.py'):
- print(f)
上面在指定目录中查找特定类型的文件, 或许是 pathlib 中最有用的功能了.
读写文件
每个 Path 实例都包含处理它所引用的文件内容的方法. 要立即检索内容, 请使用 read_bytes()或 read_text(). 要写入文件, 请使用 write_bytes()或 write_text(). 使用 open()方法打开文件并保留文件句柄, 而不是将名称传递给内置的 open()函数.
- # pathlib_read_write.py
- import pathlib
- f = pathlib.Path('example.txt')
- f.write_bytes('This is the content'.encode('utf-8'))
- with f.open('r', encoding='utf-8') as handle:
- print('read from open(): {!r}'.format(handle.read()))
- print('read_text(): {!r}'.format(f.read_text('utf-8')))
执行结果:
- read from open(): 'This is the content'
- read_text(): 'This is the content'
目录和符号链接
不存在的目录或符号链接的路径可用于创建.
如果路径已经存在, mkdir()会引发一个 FileExistsError.
- #pathlib_mkdir.py import pathlib p = pathlib.Path('example_dir') print('Creating {}'.format(p)) p.mkdir()
执行结果:
- $ python3 pathlib_mkdir.py
- Creating example_dir
- $ python3 pathlib_mkdir.py
- Creating example_dir
- Traceback (most recent call last):
- File "pathlib_mkdir.py", line 16, in <module>
- p.mkdir()
- File ".../lib/python3.5/pathlib.py", line 1214, in mkdir
- self._accessor.mkdir(self, mode)
- File ".../lib/python3.5/pathlib.py", line 371, in wrapped
- return strfunc(str(pathobj), *args)
- FileExistsError: [Errno 17] File exists: 'example_dir'
使用 symlink_to() 创建符号链接. 该链接将根据路径的值进行命名, 并将引用作为 symlink_to()的参数的名称.
- # pathlib_symlink_to.py
- import pathlib
- p = pathlib.Path('example_link')
- p.symlink_to('example.txt')
- print(p)
- print(p.resolve().name)
执行结果:
- example_link
- example.txt
文件类型
Path 实例包含几种用于测试路径引用的文件类型的方法. 本示例创建了多个不同类型的文件, 并测试这些文件以及本地操作系统上可用的一些其他设备特定的文件.
- # pathlib_types.py
- import itertools
- import os
- import pathlib
- root = pathlib.Path('test_files')
- # Clean up from previous runs.
- if root.exists():
- for f in root.iterdir():
- f.unlink()
- else:
- root.mkdir()
- # Create test files
- (root / 'file').write_text(
- 'This is a regular file', encoding='utf-8')
- (root / 'symlink').symlink_to('file')
- os.mkfifo(str(root / 'fifo'))
- # Check the file types
- to_scan = itertools.chain(
- root.iterdir(),
- [pathlib.Path('/dev/disk0'),
- pathlib.Path('/dev/console')],
- )
- hfmt = '{:18s}' + ('{:>5}' * 6)
- print(hfmt.format('Name', 'File', 'Dir', 'Link', 'FIFO', 'Block',
- 'Character'))
- print()
- fmt = '{:20s}' + ('{!r:>5}' * 6)
- for f in to_scan:
- print(fmt.format(
- str(f),
- f.is_file(),
- f.is_dir(),
- f.is_symlink(),
- f.is_fifo(),
- f.is_block_device(),
- f.is_char_device(),
- ))
is_dir(), is_file(), is_symlink(), is_socket(), is_fifo(), is_block_device(), is_char_device()都不带任何参数.
执行结果:
- Name File Dir Link FIFO Block Character
- test_files/file True False False False False False
- test_files/fifo False False False True False False
- test_files/symlink True False True False False False
- /dev/disk0 False False False False False False
- /dev/console False False False False False True
这里格式化的方法挺有意思.
文件属性
有关文件的详细信息可以使用 stat() 或 lstat() 方法来访问(用于检查可能是符号链接的状态). 这些方法产生与 os.stat() os.lstat() 相同的结果.
- # 讨论 qq 群 144081101 591302926 567351477 钉钉免费群 21745728
- import pathlib
- import sys
- import time
- if len(sys.argv) == 1:
- filename = __file__
- else:
- filename = sys.argv[1]
- p = pathlib.Path(filename)
- stat_info = p.stat()
- print('{}:'.format(filename))
- print('Size:', stat_info.st_size)
- print('Permissions:', oct(stat_info.st_mode))
- print('Owner:', stat_info.st_uid)
- print('Device:', stat_info.st_dev)
- print('Created :', time.ctime(stat_info.st_ctime))
- print('Last modified:', time.ctime(stat_info.st_mtime))
- print('Last accessed:', time.ctime(stat_info.st_atime))
执行结果:
- $ python3 pathlib_stat.py
- pathlib_stat.py:
- Size: 607
- Permissions: 0o100644
- Owner: 527
- Device: 16777218
- Created : Thu Dec 29 12:25:25 2016
- Last modified: Thu Dec 29 12:25:25 2016
- Last accessed: Thu Dec 29 12:25:34 2016
- $ python3 pathlib_stat.py index.rst
- index.rst:
- Size: 19363
- Permissions: 0o100644
- Owner: 527
- Device: 16777218
- Created : Thu Dec 29 11:27:58 2016
- Last modified: Thu Dec 29 11:27:58 2016
- Last accessed: Thu Dec 29 12:25:33 2016 False False False False False True
要更简单地访问有关文件所有者的信息, 请使用 owner()和 group().
- #pathlib_ownership.py import pathlib p = pathlib.Path(__file__) print('{} is owned by {}/{}'.format(p, p.owner(), p.group()))
执行结果:
/home/andrew/code/python-chinese-library/libraries/pathlib/pathlib_ownership.py is owned by andrew/andrew
stat() 返回数字, 这些方法将查找与 ID 相关联的名称.
touch()方法与 Unix 命令 touch 类似, 用于创建文件或更新现有文件的修改时间和权限.
- # pathlib_touch.py
- import pathlib
- import time
- p = pathlib.Path('touched')
- if p.exists():
- print('already exists')
- else:
- print('creating new')
- p.touch()
- start = p.stat()
- time.sleep(1)
- p.touch()
- end = p.stat()
- print('Start:', time.ctime(start.st_mtime))
- print('End :', time.ctime(end.st_mtime))
执行结果:
- $ python3 pathlib_touch.py
- creating new
- Start: Thu Dec 29 12:25:34 2016
- End : Thu Dec 29 12:25:35 2016
- $ python3 pathlib_touch.py
- already exists
- Start: Thu Dec 29 12:25:35 2016
- End : Thu Dec 29 12:25:36 2016
权限
在类 Unix 系统上, 可以使用 chmod()更改文件权限, 将模式作为整数传递. 模式值可以使用 stat 模块中定义的常量来构造. 这个例子切换用户的执行权限位.
脚本假定它具有运行时修改文件模式所需的权限.
- # pathlib_chmod.py
- import os
- import pathlib
- import stat
- # Create a fresh test file.
- f = pathlib.Path('pathlib_chmod_example.txt')
- if f.exists():
- f.unlink()
- f.write_text('contents')
- # Determine what permissions are already set using stat.
- existing_permissions = stat.S_IMODE(f.stat().st_mode)
- print('Before: {:o}'.format(existing_permissions))
- # Decide which way to toggle them.
- if not (existing_permissions & os.X_OK):
- print('Adding execute permission')
- new_permissions = existing_permissions | stat.S_IXUSR
- else:
- print('Removing execute permission')
- # use xor to remove the user execute permission
- new_permissions = existing_permissions ^ stat.S_IXUSR
- # Make the change and show the new value.
- f.chmod(new_permissions)
- after_permissions = stat.S_IMODE(f.stat().st_mode)
- print('After: {:o}'.format(after_permissions))
执行结果:
- $ python3 pathlib_chmod.py
- Before: 644
- Adding execute permission
- After: 744
删除
有两种从文件系统中删除东西的方法, 具体取决于类型. 要删除空目录, 请使用 rmdir().
- #pathlib_rmdir.py import pathlib p = pathlib.Path('example_dir') print('Removing {}'.format(p)) p.rmdir()
执行结果:
- $ python3 pathlib_rmdir.py
- Removing example_dir
- $ python3 pathlib_rmdir.py
- Removing example_dir
- Traceback (most recent call last):
- File "pathlib_rmdir.py", line 16, in <module>
- p.rmdir()
- File ".../lib/python3.5/pathlib.py", line 1262, in rmdir
- self._accessor.rmdir(self)
- File ".../lib/python3.5/pathlib.py", line 371, in wrapped
- return strfunc(str(pathobj), *args)
- FileNotFoundError: [Errno 2] No such file or directory:
- 'example_dir'
如果目录不存在, 则会引发 FileNotFoundError 异常. 尝试删除非空的目录也是错误的.
对于文件, 符号链接和大多数其他路径类型使用 unlink().
用户必须具有删除文件, 符号链接, 套接字或其他文件系统对象的权限.
- # pathlib_unlink.py
- import pathlib
- p = pathlib.Path('touched')
- p.touch()
- print('exists before removing:', p.exists())
- p.unlink()
- print('exists after removing:', p.exists())
执行结果:
- $ python3 pathlib_unlink.py
- exists before removing: True
- exists after removing: False
参考资料
本文相关书籍下载
本文涉及的 python 测试开发库 https://GitHub.com/china-testing/python-API-tesing 谢谢点赞!
- https://docs.python.org/3/library/pathlib.HTML
- https://pymotw.com/3/pathlib/index.HTML
代码地址
来源: http://www.jianshu.com/p/16c88cbeccca