目的:使用面向对象的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