python库介绍-pathlib: 文件系统对象

目的:使用面向对象的API解析,构建,测试和处理文件名和路径,而不是底层的字符串操作。

Path表示

pathlib包含用于管理使用POSIX标准或Microsoft Windows语法格式化的文件系统路径的类。它包括所谓的“pure”类,它们对字符串进行操作,但不与实际的文件系统进行交互;“concrete”类将API扩展为包含反映或修改本地文件系统数据的操作。

pure类PurePosixPath和PureWindowsPath可以在任何操作系统上实例化和使用,因为它们只用于名称。要实例化正确的类来处理真实的文件系统,请使用Path来获取PosixPath或WindowsPath,具体取决于平台。

  • 技术支持 (可以加钉钉pythontesting邀请加入) qq群:144081101 591302926 567351477

  • 道家技术-手相手诊看相中医等钉钉群21734177 qq群:391441566 184175668 338228106 看手相、面相、舌相、抽签、体质识别。服务费50元每人次起。请联系钉钉或者微信pythontesting

[接口自动化性能测试数据分析人工智能从业专家一对一线上培训大纲]

构建Path

要实例化路径,字符串作为参数。Path对象的字符串表示是该字符串。要创建引用相对于现有路径的值的新路径,请使用/运算符来扩展路径。运算符的参数可以是字符串或其他路径对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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)

执行结果

1
2
3
4
5
/usr
/usr/local
/usr/share
/usr/..
/etc

正如示例输出中的root值所示,操作符按照给定的方式组合路径值,并且在包含父目录引用“..”的情况下不会对结果进行解析。 但是,如果以os.sep开头它被解释为新的“根”引用。

具体路径类的resolve()方法,通过查看目录和符号链接的文件系统来解析路径,并生成名称引用的绝对路径。

1
2
3
4
5
6
7
8
# pathlib_resolve.py

import pathlib

usr_local = pathlib.Path('/usr/local')
share = usr_local / '..' / 'share'
print(type(share.resolve()))
print(share.resolve())

执行结果:

1
2
<class 'pathlib.PosixPath'>
/usr/share

joinpath()可以组合路径。

1
2
3
4
5
6
7
8
# pathlib_joinpath.py

import pathlib

root = pathlib.PurePosixPath('/')
subdirs = ['usr', 'local']
usr_local = root.joinpath(*subdirs)
print(usr_local)

执行结果:

1
2
3
$ python3 pathlib_joinpath.py

/usr/local

使用with_name()创建新路径,用不同的文件名替换路径的名称部分。 使用Use with_suffix()创建新路径,用不同的值替换文件名的扩展名。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 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)

执行结果:

1
2
3
4
5
$ python3 pathlib_from_existing.py

source/pathlib/index.rst
source/pathlib/pathlib_from_existing.py
source/pathlib/pathlib_from_existing.pyc

解析路径

Path对象具有用于从名称中提取部分值的方法和属性。例如,parts属性会生成一系列基于路径分隔符值解析的路径段。

1
2
3
4
5
6
# pathlib_parts.py

import pathlib

p = pathlib.PurePosixPath('/usr/local')
print(p.parts)

执行结果:

1
2
3
$ python3 pathlib_parts.py

('/', 'usr', 'local')

有两种方法可以从给定的路径对象中“向上”导航文件系统层次结构。父属性引用包含路径的目录的新路径实例,即os.path.dirname()返回的值。父项属性是迭代器,它产生父目录引用,不断地向上“提升”路径层次直到到达根目录。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pathlib

p = pathlib.PurePosixPath('/usr/local/lib')


print('parent: {}'.format(p.parent))

print('\nhierarchy:')
for up in p.parents:
    print(up)

执行结果:

1
2
3
4
5
6
7
8
$ python3 pathlib_parents.py

parent: /usr/local

hierarchy:
/usr/local
/usr
/

路径的其他部分可以通过路径对象的属性来访问。 name属性保存最后一个路径分隔符(与 os.path.basename()产生的值相同)后的最后一部分路径。后缀属性保存扩展分隔符后面的值,并且stem属性保留后缀之前的名称部分。

1
2
3
4
5
6
7
8
9
# 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))

执行结果:

1
2
3
4
5
6
$ 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类的实例可以通过引用文件系统上的文件,目录或符号链接的名称(或潜在名称)的字符串参数来创建。 该类还提供了几种便捷方法来构建使用常用位置(如当前工作目录和用户主目录)的实例。

1
2
3
4
5
6
7
8
9
# pathlib_convenience.py

import pathlib

home = pathlib.Path.home()
print('home: ', home)

cwd = pathlib.Path.cwd()
print('cwd : ', cwd)

执行结果:

1
2
3
home:  /home/andrew
cwd :  /home/andrew/code/python-chinese-library/libraries/pathlib
stem  : pathlib_name

目录内容

有三种方法可以访问目录列表,以发现文件系统上可用文件的名称。 iterdir()是生成器,为包含目录中的每个项目生成新的Path实例。

1
2
3
4
5
6
7
8
9
# pathlib_convenience.py

import pathlib

home = pathlib.Path.home()
print('home: ', home)

cwd = pathlib.Path.cwd()
print('cwd : ', cwd)

执行结果:

1
2
3
4
5
6
7
8
9
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()仅查找匹配模式的文件。

1
2
3
4
5
6
import pathlib

p = pathlib.Path('.')

for f in p.glob('*.py'):
    print(f)

执行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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()来进行递归扫描。

1
2
3
4
5
6
7
8
# pathlib_rglob.py

import pathlib

p = pathlib.Path('..')

for f in p.rglob('*.py'):
    print(f)

执行结果:

1
2
3
4
5
6
7
../heapq/heapq_demo.py
../_dubbo/dubbo.py
../pathlib/pathlib_name.py
../pathlib/pathlib_parents.py
../pathlib/pathlib_rglob.py
../pathlib/pathlib_parts.py
...

下面的两种用法也实现了类似的功能:

1
2
3
4
5
6
7
8
# pathlib_rglob2.py

import pathlib

p = pathlib.Path('..')

for f in p.rglob('**/*.py'):
    print(f)
1
2
3
4
5
6
7
8
# 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()函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 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')))

执行结果:

1
2
read from open(): 'This is the content'
read_text(): 'This is the content'

目录和符号链接

不存在的目录或符号链接的路径可用于创建。

如果路径已经存在,mkdir()会引发一个FileExistsError。

1
2
3
4
5
6
7
8
# pathlib_mkdir.py

import pathlib

p = pathlib.Path('example_dir')

print('Creating {}'.format(p))
p.mkdir()

执行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ 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()的参数的名称。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# pathlib_symlink_to.py

import pathlib

p = pathlib.Path('example_link')

p.symlink_to('example.txt')

print(p)
print(p.resolve().name)

执行结果:

1
2
example_link
example.txt

文件类型

Path实例包含几种用于测试路径引用的文件类型的方法。本示例创建了多个不同类型的文件,并测试这些文件以及本地操作系统上可用的一些其他设备特定的文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 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()都不带任何参数。

执行结果:

1
2
3
4
5
6
7
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() 相同的结果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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))

执行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$ 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()。

1
2
3
4
5
6
7
# pathlib_ownership.py

import pathlib

p = pathlib.Path(__file__)

print('{} is owned by {}/{}'.format(p, p.owner(), p.group()))

执行结果:

1
/home/andrew/code/python-chinese-library/libraries/pathlib/pathlib_ownership.py is owned by andrew/andrew

stat() 返回数字,这些方法将查找与ID相关联的名称。

touch()方法与Unix命令touch类似,用于创建文件或更新现有文件的修改时间和权限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 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))

执行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ 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模块中定义的常量来构造。这个例子切换用户的执行权限位。

脚本假定它具有运行时修改文件模式所需的权限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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))

执行结果:

1
2
3
4
5
$ python3 pathlib_chmod.py

Before: 644
Adding execute permission
After: 744

本文代码地址:https://bitbucket.org/china-testing/python-chinese-library/src/e8a0671ecdb6c2b24d561f155415e102d6c40cbf/libraries/pathlib/?at=default

本文最新版本地址:http://t.cn/R8LireQ

交流QQ群:python 测试开发 144081101

wechat: pythontesting

淘宝天猫可以把链接发给qq850766020,为你生成优惠券,降低你的购物成本!

快来领取支付宝跨年红包!1月1日起还有机会额外获得专享红包哦!复制此消息,打开最新版支付宝就能领取!2C56CV70sA

删除

有两种从文件系统中删除东西的方法,具体取决于类型。要删除空目录,请使用rmdir()。

1
2
3
4
5
6
7
8
# pathlib_rmdir.py

import pathlib

p = pathlib.Path('example_dir')

print('Removing {}'.format(p))
p.rmdir()

执行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ 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()。

用户必须具有删除文件,符号链接,套接字或其他文件系统对象的权限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 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())

执行结果:

1
2
3
4
$ python3 pathlib_unlink.py

exists before removing: True
exists after removing: False

links