Pathlib:将文件系统路径作为对象使用
《pathlib —— Object-oriented filesystem paths》
目前,Python 科学栈中的所有主要项目都同时支持 Python 3.x 和 Python 2.7,不过,这种情况很快即将结束。去年 11 月,Numpy 团队的一份声明引发了数据科学社区的关注:这一科学计算库即将放弃对于 Python 2.7 的支持,全面转向 Python 3。Numpy 并不是唯一宣称即将放弃 Python 旧版本支持的工具,pandas 与 Jupyter notebook 等很多产品也在即将放弃支持的名单之中。【新闻链接】
根据近期新闻,隐隐觉得必须尽早熟识掌握进阶到Python3的特性与区别,今天看了看文件路径方面,发现这个还挺好用的自带库,那么来看看它的特性吧。
面向对象编程的第一步,自然是需要获取pathlib的实例:
不传参时默认使用当前位置Path('.')
,也可传一个或多个字符串自动拼接
from pathlib import Path
q = Path() # WindowsPath('.')
q = Path('setup.py') # WindowsPath('setup.py')
q = Path('test', 'setup.py') # WindowsPath('test/setup.py')
这里需要知道的是,在不同的OS下,会自动适配不同的对象类型:
Unix族的OS上是PosixPath
,Windows上是WindowsPath
Path('setup.py')
# PosixPath('setup.py') # on a Unix machine
Path('setup.py')
# WindowsPath('setup.py') # on a Windows machine
特别的,如果传参包含绝对路径:
# 都是绝对路径的话,会取最后一个
Path('c:/Windows', 'd:bar')
# WindowsPath('d:bar')
Path('c:/Windows', '/Program Files')
WindowsPath('c:/Program Files')
比较醒目而直观的一点,是可以简单的将路径字符串通过Pathlib实例化后,简单的使用除法操作符(/
),就可以与字符串或是同为Pathlib实例的对象进行拼接操作。
# pathlib_operator.py
usr = Path.PurePosixPath('/usr')
print(usr) # /usr
usr_local = usr / 'local'
print(usr_local) # /usr
usr_share = usr / Path.PurePosixPath('share')
print(usr_share) # /usr/share
root = usr / '..'
print(root) # /usr/..
etc = root / '/etc/'
print(etc) # /etc
对于文件遍历,我们平时那些常用的组合拳,放在前面备查备忘;
详细的成员函数和属性后面再列个清单好了。
p.resolve()
可以理解为一个anchor或是一个cursor
在文件系统里按照路径的顺序,获得这一路走下去走完之后的路径。
如下述示例代码中:
'/usr/local'
在返回上一级'..'
后成为'/usr'
'/usr'
在拼接'share'
后成为'usr/share'
usr_local = Path('/usr/local')
share = usr_local / '..' / 'share'
print(share.resolve()) # 'usr/share'
p = Path('.')
[x for x in p.iterdir() if x.is_dir()]
# [ PosixPath('.hg'),
# PosixPath('docs'), PosixPath('dist'),
# PosixPath('__pycache__'), PosixPath('build') ]
p = Path('.')
list(p.glob('**/*.py'))
# [ PosixPath('test_pathlib.py'), PosixPath('setup.py'),
# PosixPath('pathlib.py'), PosixPath('docs/conf.py'),
# PosixPath('build/lib/pathlib.py') ]
获取当前路径cwd()
与home路径home()
Path.cwd()
# WindowsPath('C:/Users/okcd0')
Path.home()
# WindowsPath('C:/Users/okcd0')
与先前的路径判断相似,可以调用对象的成员函数来获得这些信息:
q.exists() # 是否存在
q.is_dir() # 是否文件夹
q.is_file() # 是否文件
q.is_absolute() # 是否绝对路径
q.match('*.py') # 是否匹配该正则
获取当前Path对象所指目标的信息:
如文件权限chmod
、所属用户owner
:
p = Path('setup.py')
p.owner() # okcd0
p.stat().st_size # 956
p.stat().st_mtime # 1327883547.852554
p.stat().st_mode # 33277
p.chmod(0o444)
p.stat().st_mode # 33060
这个比起以往的要方便的多了,分析路径的时候不再需要字符串级别处理:
q = Path('test', 'setup.py') # WindowsPath('test/setup.py')
q.parts # ('test', 'setup.py')
除了parts
可以分割开以外,还有一些便捷的函数可以直接获得属性:
如path
,name
,suffix
,stem
(路径、文件名、后缀、去掉后缀的文件名)
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))
# path : source/pathlib/pathlib_name.py
# name : pathlib_name.py
# suffix: .py
# stem : pathlib_name
关于suffix
还有一个suffixes
的用法:
Path('my/library.tar.gar').suffix
# '.gar'
Path('my/library.tar.gar').stem
# 'my/library.tar'
Path('my/library.tar.gar').suffixes
# ['.tar', '.gar']
Path('my/library.tar.gz').suffixes
# ['.tar', '.gz']
Path('my/library').suffixes
# []
再说在Windows里,还有一些与盘符相关的属性:
如drive
,root
,anchor
(盘符,根目录,锚点)
Path('c:/Program Files/').drive # 'c:'
Path('/Program Files/').drive # ''
Path('/etc').drive # ''
当我们获得某个路径对象后,可以替换掉其中特定部分:
如下述示例代码中:
通过with_name()
替换文件名部分(注意文件名是带后缀的)
通过with_suffix()
替换后缀部分
ind = Path('source/pathlib/index.rst')
print(ind) # source/pathlib/index.rst
py = ind.with_name('pathlib_from_existing.py')
print(py) # source/pathlib/pathlib_from_existing.py
pyc = py.with_suffix('.pyc')
print(pyc) # source/pathlib/pathlib_from_existing.pyc
返回一个可迭代的
对象,可以迭代或下标获取:
Path('test', 'setup.py').parents[0] # 'test'
Path('test', 'setup.py').parents[1] # '.'
类似的使用实例化的文件路径对象来进行文件的读写,我们常用的方式如下:
q = Path('example.txt')
with q.open() as f:
f.readline()
# '#!/bin/bash\n'
而Pathlib库提供了一个更加便捷的方法:
f = Path('example.txt')
f.write_bytes('This is the content'.encode('utf-8'))
# f.write_text('This is the content')
with f.open('r', encoding='utf-8') as handle:
print('read from open(): {!r}'.format(handle.read()))
# read from open(): 'This is the content'
print('read_text(): {!r}'.format(f.read_text('utf-8')))
# read_text(): 'This is the content'