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

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

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容