对文件的路径操作是一个非常基础的问题,但也是一个至关重要的问题,优雅的路径操作不仅可以让代码可读性更高;还可以让用户避免很多不必要的麻烦。python中路径操作常用的几种方式重要包括:字符串拼接、os.path
、以及python 3.4中新增的面向对象的路径操作库 pathlib。
字符串拼接
字符串拼接是最原始、最不优雅的路径操作方式,也是很多初学者偏爱的方式,但是强烈推荐使用以下两种更优雅的路径操作方式。因为不推荐字符串拼接,所以下面简要列举一下常见的拼接方式(更多字符串操作方法请见Python字符串处理)。
In [2]: directory = '/home/jeffery0207'
In [3]: filename = 'a.txt'
In [4]: directory + '/' + filename
Out[4]: '/home/jeffery0207/a.txt'
In [5]: '/'.join([directory, filename])
Out[5]: '/home/jeffery0207/a.txt'
In [6]: f'{directory}/{filename}' # python3.6之后新增
Out[6]: '/home/jeffery0207/a.txt'
In [7]: '{0}/{1}'.format(directory, filename)
Out[7]: '/home/jeffery0207/a.txt'
In [8]: '%s/%s' % (directory, filename)
Out[8]: '/home/jeffery0207/a.txt'
os.path
os.path
模块是个人比较常用的一个模块,因为Unix系统和windows系统上的路径表征有一定的差别,加载该模块时python会自动根据所使用的的系统加载不同版本的os.path
(posixpath
对应于Unix-style的路径处理;ntpath
对应于Windows-style的路径处理)。该模块实现了一些实用的路径处理函数,主要包括:
__all__ = ['expanduser', 'expandvars',
'abspath', 'realpath', 'relpath',
'split', 'splitdrive', 'splitext', 'basename', 'dirname', 'join',
'getatime', 'getctime', 'getmtime', 'getsize',
'isabs', 'isdir', 'isfile', 'islink', 'ismount', 'exists', 'lexists',
'samefile', 'sameopenfile', 'samestat',
'normcase', 'normpath',
'commonpath', 'commonprefix']
-
expanduser()
和expandvars()
函数python默认不会识别shell变量及家目录符
~
,可以通过这两个函数实现扩展In [1]: expandvars('$HOME/workspace') Out[1]: '/home/liunianping/workspace' In [2]: expanduser('~/workspace') Out[2]: '/home/liunianping/workspace'
-
split()
,splitdrive()
,splitext()
,basename()
,dirname()
和join()
该模块中最好用的一组函数,常用于路径拼接
In [3]: split(path) Out[3]: ('/Users/jeffery0207', 'a.b.c.txt') In [4]: splitext(path) Out[4]: ('/Users/jeffery0207/a.b.c', '.txt') In [5]: splitdrive(path) Out[5]: ('', '/Users/jeffery0207/a.b.c.txt') In [6]: basename(path) Out[6]: 'a.b.c.txt' In [7]: dirname(path) Out[7]: '/Users/jeffery0207' In [8]: join(dirname(path), 'text.txt') Out[8]: '/Users/jeffery0207/text.txt'
-
isabs()
,isdir()
,isfile()
,islink()
,ismount()
,exists()
,lexists()
该模块中用于路径处理判断的一组函数
In [9]: isabs(expanduser('~/.zshrc')),isabs('./.zshrc') # 判断给定路径是否是绝对路径 Out[9]: (True, False) In [11]: isdir('./.local') # 判断给定路径是否是文件夹类型 Out[11]: True In [12]: isfile('./.local') # 判断给定路径是否是普通文件 Out[12]: False In [13]: islink('./miniconda3/bin/python') # 判断给定路径是否是符号链接文件 Out[13]: True In [14]: ismount('/media/disk01') # 判断给定路径是否是挂载点 Out[14]: True In [15]: exists('./miniconda3/bin/python'), lexists('./miniconda3/bin/python') # 当判断一个链接文件是否存在时,如果所链接那个文件不存在时,前者返回False,后者返回True Out[15]: (True, True)
-
getatime()
,getctime()
,getmtime()
和getsize()
依次指:返回上次访问该path的时间;返回该path的系统ctime,在unix系统上对应于该path上次元数据更改的时间,在windows上对应文件的创建时间;返回该path上一次修改的时间;返回该path的文件大小
In [16]: path = './.zshrc' In [17]: getatime(path), getctime(path), getmtime(path), getsize(path) Out[17]: (1634019401.9940903, 1633942149.2245831, 1633942149.2205787, 4506)
-
normcase()
,normpath()
In [18]: normcase(path) # 在windows系统上将所有字符小写,并将所有正斜杠转换为反斜杠;其他系统返回不变的path Out[18]: './.zshrc' In [19]: join('./a/', './b.txt') Out[19]: './a/./b.txt' In [20]: normpath(join('./a/', './b.txt')) # 折叠多余的分隔符,在这种情况下非常有用 Out[20]: 'a/b.txt'
-
commonpath()
,commonprefix()
In [27]: commonpath(['./a/b/c.1.txt', './a/b/c.2.txt']) Out[27]: 'a/b' In [28]: commonprefix(['./a/b/c.1.txt', './a/b/c.2.txt']) Out[28]: './a/b/c.'
-
samefile(path1, path2)
,sameopenfile(fp1, fp2)
,samestat(stat1, stat2)
我们可以创建如下两个文件作为演示:
>>> mkdir test && cd test >>> touch a.txt >>> ln -s a.txt a.link.txt >>> ll # lrwxr-xr-x 1 jeffery0207 staff 5B Oct 12 21:47 a.link.txt -> a.txt # -rw-r--r-- 1 jeffery0207 staff 0B Oct 12 21:47 a.txt
首先我们需要了解一下另外三个函数,
os.fstat(fd)
,os.lstat(path, *, dir_fd=None)
,os.stat(path, *, dir_fd=None, follow_symlinks=True)
:-
os.stat(path, *, dir_fd=None, follow_symlinks=True)
该函数返回一个
stat_result
对象(stat_result
详细属性请见os.stat_result),表征一个文件(file)或则文件描述符(file descriptor)的状态,等效于系统调用stat
:$ stat a.txt # mac系统的显示 16777223 12934280785 -rw-r--r-- 1 jeffery staff 0 0 "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" 4096 0 0 a.txt $ stat a.txt # linux 系统的显示 File: ‘a.txt’ Size: 4 Blocks: 8 IO Block: 4096 regular file Device: fd0ah/64778d Inode: 69309229738 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 1042/jeffery) Gid: ( 110/ staff) Access: 2021-12-02 22:01:41.000000000 +0800 # 最后访问时间 Modify: 2021-12-02 22:01:40.000000000 +0800 # 最后修改时间 Change: 2021-12-03 10:10:28.992397846 +0800 # 最后状态改变时间 Birth: # 创建时间
该函数默认会跟踪符号链接文件,要获得符号链接文件本身的状态,需要设置参数
follow_symlinks=False
或则使用os.lstat
-
os.lstat(path, *, dir_fd=None)
该函数返回一个
stat_result
对象,表征一个文件(file)的状态。从python3.3起,os.lstat(path, *, dir_fd=None)
函数等同于os.stat(path, dir_fd=dir_fd, follow_symlinks=False
函数。 -
os.fstat(fd)
该函数返回一个
stat_result
对象,表征一个文件描述符(file descriptor)的状态。从 Python 3.3 开始,这等效于os.stat(fd)
samestat(stat1, stat2)
检测两个stat_result
对象是否指向同一个文件:In [30]: a_link_stat = stat('./test/a.link.txt') In [31]: a_stat = stat('./test/a.txt') In [32]: a_link_lstat = lstat('./test/a.link.txt') In [33]: samestat(a_link_stat, a_stat), samestat(a_link_lstat, a_stat) Out[33]: (True, False)
而
samefile(path1, path2)
函数即底层调用os.stat(path, *, dir_fd=None, follow_symlinks=True)
检测两个文件是否指向同一个文件:In [34]: samefile('./test/a.link.txt', './test/a.txt') Out[34]: True
sameopenfile(fp1, fp2)
则检测两个文件描述符是否是指同一个文件,是则返回True
,反之返回False
。其先通过依次调用fstat()
函数和samestat()
函数判断个文件描述符是否是指同一个文件。In [34]: import os In [35]: fd_a = os.open('./test/a.txt', os.O_RDWR) In [36]: fd_a_link = os.open('./test/a.link.txt', os.O_RDWR) In [37]: sameopenfile(fd_a, fd_a_link) Out[37]: True
-
-
abspath(path)
,relpath(path, start=os.curdir))
,realpath(path, *, strict=False)
分别对应绝对路径 (等效于
normpath(join(os.getcwd(), path))
)、相对路径(返回path
相对于start
文件夹的路径)、真实路径(该函数用于解析链接文件的真实路径,当strict=False
时,如果path不存在或遇到符号链接循环则会抛出OSError
错误)。In [39]: abspath(path) Out[39]: '/Users/jeffery/.zshrc' In [40]: relpath(abspath(path), start='./') # start 参数默认值为os.curdir,即当前文件夹 Out[40]: '.zshrc' In [41]: realpath('./miniconda3/bin/python') # 一般,我们的python其实是一个链接文件,对应一个具体的python版本文件 Out[41]: '/Users/jeffery/miniconda3/bin/python3.9'
pathlib
pathlib是python3.4中新增的面向对象的文件系统路径操作模块,为不同文件系统提供较为统一的文件系统路径操作方法。该模块主要定义了以下类(class),其中pure path包括PurePath
, PurePosixPath
, PureWindowsPath
; concrete paths 包括Path
,PosixPath
以及WindowsPath
,其继承关系如下图所示,PurePath
为其他所有类的父类;Path
类继承于PurePath
,同时作为PosixPath
,WindowsPath
的子类。
因此,pure paths和concrete paths主要区别在于Path
类的方法,pure paths类主要封装了对路径本身的操作,而concrete paths还包括对文件系统地操作,如新建、删除等。这种继承关系可以很好的分离路径操作和文件系统的操作,更多请参考之前的博客python面向对象编程
PurePath方法和属性-路径操作
drive | root | anchor | parents |
name | suffix | suffixes | stem |
as_uri() | is_absolute() | is_relative_to(*other) | is_reserved() |
match(pattern) | relative_to(*other)¶ | with_name(name) | with_stem(stem) |
In [1]: from pathlib import PurePath, PurePosixPath, PureWindowsPath
In [2]: p = PurePath('/Users/jeffery/.zshrc')
In [3]: p1 = PurePath('/Users/jeffery/a.b.c.txt')
In [4]: p2 = PureWindowsPath('c:/foo/bar/setup.py')
-
drive
In [5]: p.drive, p2.drive # Unix-style的路径没有盘的概念 Out[5]: ('', 'c:')
-
root
In [6]: p.root, p2.root Out[6]: ('/', '\\')
-
anchor
In [7]: p1.anchor, p2.anchor # anchor 就是指 drive和root两者 Out[7]: ('/', 'c:\\')
-
parents
该属性是路径的所有父目录,返回一个Iterable对象
In [8]: pars = p.parents In [9]: from collections import Iterable In [11]: isinstance(pars, Iterable) Out[11]: True In [12]: for par in pars: ...: print(par) ...: /Users/jeffery /Users /
-
parent
该属性指路径的父目录,
p.parent
则是/Users/jeffery
-
name
该属性指路径最后一层级的名称,但不包括drive和root,可方便的用于取文件名或文件夹名
In [13]: p1.name Out[13]: 'a.b.c.txt' In [14]: PurePath('/').name Out[14]: ''
-
suffix
In [15]: p.suffix # 取文件扩展名 Out[15]: '' In [16]: p1.suffix Out[16]: '.txt'
-
suffixes
In [17]: p1.suffixes # 取多层级的文件扩展名,其本质就是根据.进行字符串分割 Out[17]: ['.b', '.c', '.txt']
-
stem
相当于先取
path.name
,然后再去除文件扩展名(path.suffix
)In [18]: p1.stem Out[18]: 'a.b.c'
-
as_posix()
将路径转换成斜杆
/
的形式In [19]: p = PureWindowsPath('c:\\windows') In [20]: str(p) Out[20]: 'c:\\windows' In [21]: p.as_posix() Out[21]: 'c:/windows'
-
as_uri()
将路径转换成文件url的形式,参数
path
必须是绝对路径,否则抛出ValueError
In [22]: p1.as_uri() Out[22]: 'file:///Users/jeffery/a.b.c.txt'
-
is_absolute()
判断一个路径是否是绝对路径的形式
-
is_relative_to(*other)
该方法判断路径是否和另一个路径是相对关系,换句话说就是
other
是否是路径的父目录或则相同目录(当路径本身是目录时)In [26]: p1.is_relative_to('/Users/jeffery') Out[26]: True In [27]: p1.is_relative_to('/Users') Out[27]: True In [28]: p1.is_relative_to('/Users/jeffery/c.txt') Out[28]: False
-
is_reserved()
判断一个目录是否是操作系统中一些特殊的文件设备名,如nul, com1, com2等,该命令只针对windows-like path, Posix-like path总是返回
False
.In [35]: PureWindowsPath('nul').is_reserved() Out[35]: True
-
joinpath(*other)
路径名连接,等效于操作符
/
In [32]: PurePosixPath('/etc').joinpath('init.d', 'apache2') Out[32]: PurePosixPath('/etc/init.d/apache2') In [33]: PurePosixPath('/etc') / 'init.d' / 'apache2' Out[33]: PurePosixPath('/etc/init.d/apache2')
-
match(pattern)
路径名称匹配
In [34]: PurePath('/a/b/c.py').match('b/*.py') Out[34]: True In [35]: PurePath('/a/b/c.py').match('a/*.py') Out[35]: False In [36]: PurePath('/a/b/c.py').match('/a/*.py') # 特别注意这一点,和shell的匹配模式不一样 Out[36]: False
-
relative_to(*other)¶
以
other
路径为起点,计算路径的相对路径,如果无法计算则抛出ValueError
In [39]: p.relative_to('/') Out[39]: PurePosixPath('etc/passwd') In [40]: p.relative_to('/etc/passwd') Out[40]: PurePosixPath('.')
以上代码等效于
os.path.relpath
:In [42]: from os.path import relpath In [43]: relpath('/etc/passwd', start='/') Out[43]: 'etc/passwd' In [44]: relpath('/etc/passwd', start='/etc/passwd') Out[44]: '.'
-
with_name(name)
该方法将路径中原本的
name
替换为新的name
,如果路劲中本身不存在name,则抛出ValueError
错误In [45]: p1.with_name('new.txt') Out[45]: PurePosixPath('/Users/jeffery/new.txt') ## 用os.path实现 In [46]: from os.path import join, split In [47]: join(split('/Users/jeffery/a.b.c.txt')[0], 'new.txt') Out[47]: '/Users/jeffery/new.txt'
-
with_stem(stem)
该方法将路径中原本的
stem
替换为新的stem
,如果路劲中本身不存在name,则抛出ValueError
错误In [51]: p1.with_stem('new') Out[51]: PurePosixPath('/Users/jeffery/new.txt') ## 用os.path实现 In [52]: from os.path import join, split, splitext In [53]: join(split("/Users/jeffery/a.b.c.txt")[0],'new' + splitext("/Users/jeffery/a.b.c.txt")[1]) Out[53]: '/Users/jeffery/new.txt'
-
with_suffix(suffix)
该方法将路径中原本的
suffix
替换为新的suffix
,如果suffix=""
,则原本的suffix直接移除In [54]: p1.with_suffix('.py') Out[54]: PurePosixPath('/Users/jeffery/a.b.c.py')
ConcretePath方法和属性-文件系统操作
为了探索ConcretePath方法和属性,我们首先在家目录下建一个新的文件tmp
, 并在tmp
下新建文件a.txt
。
In [1]: from pathlib import PosixPath, Path, WindowsPath
In [2]: p1 = PosixPath('./tmp')
In [3]: p2 = Path('./tmp/a.txt')
-
cwd()
该方法返回当前所在文件夹,等同于
os.getcwd()
,因此可以知道该方法应该是一个classmethod,其调用与具体的对象无关In [4]: Path.cwd(), PosixPath.home() Out[4]: (PosixPath('/Users/jeffery'), PosixPath('/Users/jeffery'))
-
home()
该方法也是一个classmethod,返回用户的家目录路径,等同于
os.path.expanduser()
-
stat(*, follow_symlinks=True)
该方法类似于前面介绍的
os.stat()
,返回一个os.stat_result
对象(stat_result
详细属性请见os.stat_result)。所以,类似的,follow_symlinks=False
时,可以统计链接文件的信息, 等效于os.lstat
。In [5]: stat_result = p2.stat() In [6]: stat_result Out[6]: os.stat_result(st_mode=33188, st_ino=12936268394, st_dev=16777223, st_nlink=1, st_uid=501, st_gid=20, st_size=15, st_atime=1637994757, st_mtime=1637994751, st_ctime=1637994751)
-
chmod(mode, *, follow_symlinks=True)
修改路径权限,类似于
os.chmod()
或则shell 命令chmod
。这里我们使用In [10]: p2.chmod(0o655) # (python3)这里使用8进制,更为简明 In [11]: ll tmp/ -rw-r-xr-x 1 jeffery staff 15 Nov 27 14:32 a.txt
-
exists()
判断路径是否存在,值得注意的是,当路径是链接文件时,该方法会进一步追踪查看对应的符号链接是否存在
-
expanduser()
同上面的
os.path.expanduser()
,如果家目录无法确定的话,则抛出RuntimeError
-
glob(pattern)
根据pattern搜索路径中匹配的文件,
pattern
的匹配规则同fnmatch,主要利用以下unix shell-like通配符进行简单的文件匹配任务(如更复杂的任务,请参考python re 正则表达式):Pattern Meaning *
matches everything ?
matches any single character [seq]
matches any character in seq [!seq]
matches any character not in seq glob(pattern)
方法返回一个generator类型,可以通过迭代的方法取出所有匹配的文件:In [13]: g = p1.glob('*.txt') In [14]: for _g in g: ...: print(_g) ...: tmp/a.txt In [15]: type(g) Out[15]: generator
python标准库glob中
glob.glob()
及glob.iglob()
函数也是利用unix shell-like 通配符进行文件匹配。前者返回数据类型为list,后者返回类型为iterator。In [16]: from glob import glob, iglob, escape In [17]: glob('./tmp/*.txt') Out[17]: ['./tmp/a.txt'] In [18]: iglob('./tmp/*.txt') Out[18]: <generator object _iglob at 0x110e8e510>
需要注意的是,
glob.glob()
默认情况下是不会匹配隐藏文件的;而Path.glob()会匹配隐藏文件:In [19]: for _g in p1.glob('*.txt'): ...: print(_g) ...: tmp/.a.txt tmp/a.txt In [20]: glob('./tmp/*.txt') Out[20]: ['./tmp/a.txt']
另外,当文件匹配的路径中含有通配符时,我们可以借助
glob.escape()
函数进行快速注释,比如我们新建文件mkdir -p "./tmp/[ABC]/b.txt"
,则我们需要利用以下方式进行匹配:In [21]: from os.path import join In [22]: glob(join(escape('./tmp/[ABC]'), '*.txt')) Out[22]: ['./tmp/[ABC]/b.txt']
-
group()
返回文件所属组别
-
is_dir()
判断路径是否是文件夹,返回bool值;需要注意的是如果文件不存在或符号链接文件有问题时,也返回False(以下类似函数也适用该规则)。
-
is_file()
判断路径是否是文件,返回bool值
-
is_mount()
判断路径是否是挂载点,返回bool值
-
is_symlink()
判断路径是否是符号链接文件,返回bool值
-
is_socket()
判断路径是否是socket连接,返回bool值
-
is_fifo()
判断路径是否是FIFO管道文件,返回bool值
-
is_block_device()
判断路径是否是块设备文件,返回bool值
-
is_char_device()
判断路径是否是字符设备文件,返回bool值
-
iterdir()
遍历文件夹,并返回路径中所有的文件
In [23]: for _dir in p1.iterdir(): ...: print(_dir) ...: tmp/.a.txt tmp/a.txt tmp/[ABC]
-
lchmod(mode)
不同于
Path.chmod(mode)
默认效果,该函数修改文件权限时,如果路径是链接文件,不会同时修改对应符号链接文件的权限 -
lstat()
不同于
Path.stat()
,当路径是链接文件时,返回链接文件的统计信息而非它的目标文件 -
mkdir(mode=511, parents=False, exist_ok=False)
创建文件夹
-
open(mode=‘r’, buffering=- 1, encoding=None, errors=None, newline=None)
打开文件,类似于python内置函数
open()
-
owner()
返回路径所属用户
-
read_bytes()
读取bytes,相当于打开并读取两步操作
In [24]: p2.read_bytes() Out[24]: b'This is a bird\n' In [25]: open('./tmp/a.txt', 'rb').read() Out[25]: b'This is a bird\n'
-
read_text(encoding=None, errors=None)
类似于
read_bytes()
,但是以text形式读取文件 -
readlink()
该方法返回路径的目标链接文件,如果路径不是链接文件,则抛出
OSError
In [56]: ll ./tmp/ # 命令行新建文件a.link.txt ln -s a.txt a.link.txt total 8 drwxr-xr-x 3 jeffery staff 96 Nov 30 11:27 [ABC]/ lrwxr-xr-x 1 jeffery staff 5 Nov 30 15:03 a.link.txt@ -> a.txt -rw-r-xr-x 1 jeffery staff 15 Nov 30 11:16 a.txt* In [54]: Path('./tmp/a.link.txt').readlink() Out[54]: PosixPath('a.txt')
-
rename(target)
重命名文件,返回值为更新后指向
target
的对象,如果target
所对应的路径已经存在,在权限允许下,会直接覆盖。target
参数既可以是字符串类型也可以是path对象。target
参数可以是相对路径,也可以是绝对路径;当相对路径时,相对的是Path.cwd()
而不是path 对象。In [55]: p = Path('foo') In [56]: p.open('w').write('some text') Out[56]: 9 In [57]: target = Path('bar') In [58]: p.rename(target) Out[58]: PosixPath('bar') In [59]: target.open().read() Out[59]: 'some text'
-
replace(target)
同上,重命名文件,返回值为更新后指向
target
的对象。如果target
对象已经存在,则会无条件的覆盖 -
resolve(strict=False)
返回路径的绝对路径,如果路径是链接文件,则返回路径对应符号链接文件的绝对路径
In [60]: p2.resolve() Out[60]: PosixPath('/Users/jeffery/tmp/a.txt') In [61]: Path('./tmp/a.link.txt').resolve() Out[61]: PosixPath('/Users/jeffery/tmp/a.txt')
strict=False
时,如果文件不存在,它也会强行解析出其路径;strict=True
时,如果文件不存在,则抛出FileNotFoundError
错误In [62]: Path('./tmp/text.txt.zip').resolve() Out[62]: PosixPath('/Users/jeffery/tmp/text.txt.zip') In [63]: Path('./tmp/text.txt.zip').resolve(strict=True) FileNotFoundError: [Errno 2] No such file or directory: '/Users/jeffery/tmp/text.txt.zip'
-
rglob(pattern)
该方法等同于
Path.glob("**/{pattern}")
-
rmdir()
删除文件夹,文件夹必须为空
-
samefile(other_path)
返回bool值,判断路径与
other_path
是否指向同一个文件。如果任一文件不存在或权限问题而无法访问,则抛出OSError
。该方法类似于上面探讨到的os.path.samefile()
和os.path.samestat()
。 -
symlink_to(target, target_is_directory=False)
该方法链接路径到
target
路径,当target
是文件夹时,在windows下需要设置target_is_directory=True; 在unix环境下,该参数是忽略的。In [64]: Path('./tmp/link.txt').symlink_to('./tmp/a.txt') -rw-r-xr-x 1 jeffery staff 15B Nov 30 11:16 a.txt lrwxr-xr-x 1 jeffery staff 11B Dec 2 21:43 link.txt -> ./tmp/a.txt
-
hardlink_to(target)
类似于
symlink_to()
,该方法创建硬链接 -
link_to(target)
与
hardlink_to()
相反,该方法中target
作为硬链接文件链接到对象对应路径In [65]: p2.link_to('./tmp/hard.link.txt') -rw-r-xr-x 2 jeffery staff 15B Nov 30 11:16 a.txt -rw-r-xr-x 2 jeffery staff 15B Nov 30 11:16 hard.link.txt
-
touch(mode=438, exist_ok=True)
创建文件,当
exist_ok=True
且对应文件已存在时,会抛出FileExistsError
。 -
unlink(missing_ok=False)
删除文件或移除符号链接文件,当
missing_ok=False
且路径不存在时,抛出FileNotFoundError
-
write_bytes(data)
read_bytes
反操作,写入bytes,相当于打开并写入bytes两步操作In [66]: p2.write_bytes(b'xxxx') # 覆盖式写入 Out[66]: 4
-
write_text(data, encoding=None, errors=None, newline=None)
类似于
write_bytes
,但是以text形式写入文件
对照表
python documentation官方网站也给出了pathlib库与os及os.path模块实现相同功能函数的对照表如下,如果你已经熟悉后者,可以依据该表格快速的进行”迁移学习“。
os and os.path | pathlib |
---|---|
os.path.abspath() | Path.resolve() |
os.chmod() | Path.chmod() |
os.mkdir() | Path.mkdir() |
os.makedirs() | Path.mkdir() |
os.rename() | Path.rename() |
os.replace() | Path.replace() |
os.rmdir() | Path.rmdir() |
os.remove(), os.unlink() | Path.unlink() |
os.getcwd() | Path.cwd() |
os.path.exists() | Path.exists() |
os.path.expanduser() | Path.expanduser() and Path.home() |
os.listdir() | Path.iterdir() |
os.path.isdir() | Path.is_dir() |
os.path.isfile() | Path.is_file() |
os.path.islink() | Path.is_symlink() |
os.link() | Path.hardlink_to() |
os.symlink() | Path.symlink_to() |
os.readlink() | Path.readlink() |
os.path.relpath() | Path.relative_to() 2 |
os.stat() | Path.stat(), Path.owner(), Path.group() |
os.path.isabs() | PurePath.is_absolute() |
os.path.join() | PurePath.joinpath() |
os.path.basename() | PurePath.name |
os.path.dirname() | PurePath.parent |
os.path.samefile() | Path.samefile() |
os.path.splitext() | PurePath.suffix |
权限码转换
前面有个例子,涉及到python中权限编码的问题,这里进一步展开开探讨一下:
In [1]: from pathlib import Path
In [2]: p = Path('./tmp/a.txt')
In [3]: sr =p.stat()
In [4]: sr
Out[4]: os.stat_result(st_mode=33188, st_ino=12936268394, st_dev=16777223, st_nlink=1, st_uid=501, st_gid=20, st_size=4, st_atime=1638497905, st_mtime=1638453700, st_ctime=1638497208)
在命令行中,我们同样可以查看到文件的权限详情:
$ ll a.txt
-rw-r--r-- 1 jeffery staff 4B Dec 2 22:01 a.txt
那么问题来了,st_mode=33188
和-rw-r--r--
;以及st_mode=33188
和644具有怎样的对应关系呢?
首先,我们可以利用标准库stat
模块中的``函数进行转换:
In [5]: import stat
In [6]: stat.filemode(stat_result.st_mode) # 33188 代表的权限就是644
Out[6]: '-rw-r--r--'
但是st_mode=33188
包含的信息其实不仅仅包括文件读写权限的信息:
In [5]: oct(sr.st_mode) # 转换成八进制,我们注意到八进制包含644,其实八进制最后三位对应的就是文件的读写权限编码
Out[5]: '0o100644'
st_mode=33188
是一个十进制数,转换成八进制后,我们注意到八进制包含644,其实八进制最后三位对应的正是文件的读写权限编码,stat.filemode()
正是利用该特性进行权限字符串转换的,规则如下:
S_IRWXU 00700 mask for file owner permissions
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 mask for group permissions
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 mask for permissions for others (not in group)
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
而'0o100644'
前面的部分0100000
代表的则是文件类型—regular file,具体规则如下:
S_IFMT 0170000 bitmask for the file type bitfields
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
S_ISUID 0004000 set UID bit
S_ISGID 0002000 set-group-ID bit
S_ISVTX 0001000 sticky bit
写在篇后
这篇博客内容本身非常简单,但是所提到的两个模块包含了大量的API,需要我们在日常的使用中灵活应用,选择最高效、优雅的方式。其中,pathlib模块面向对象编程的路径处理方式,能够非常从容的应对较大的项目开发需求,高度统一路径操作、文件系统操作,比如单细胞转录组分析分析软件scanpy中便是采用了pathlib库进行路径处理工作,是项目开发中不可多得的好例子。