文件路徑操作是一個非常基礎但重要的問題,優雅的路徑操作不僅可以讓代碼可讀性更高;還可以讓用戶避免很多不必要的麻煩。python中路徑操作包括三類方法:1. 字符串拼接、2. os.path、3. python 3.4中新增的面向對象的路徑操作庫 pathlib。
本文內容較長且繁瑣,對於初學者建議三個部分循序漸進學習。
字符串拼接字符串拼接是最原始、最不優雅的路徑操作方式,也是很多初學者偏愛的方式,強烈推薦使用以下兩種更優雅的路徑操作方式。這裡,簡要列舉一下常見的拼接方式(更多字符串操作方法請見Python字符串處理[1])。
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模塊是個人比較常用的一個模塊,因為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)
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'
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[2]),表徵一個文件(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是python3.4中新增的面向對象的文件系統路徑操作模塊,為不同文件系統提供較為統一的文件系統路徑操作方法。該模塊主要定義了以下類(class),其中pure path包括PurePath, PurePosixPath, PureWindowsPath; concrete paths 包括Path,PosixPath以及WindowsPath,其繼承關係如下圖所示,PurePath 為其他所有類的父類;Path類繼承於PurePath,同時作為PosixPath,WindowsPath的子類。
from python documentation因此,pure paths和concrete paths主要區別在於Path類的方法,pure paths類主要封裝了對路徑本身的操作,而concrete paths還包括對文件系統地操作,如新建、刪除等。這種繼承關係可以很好的分離路徑操作和文件系統的操作,更多請參考之前的推文python面向對象編程
PurePath方法和屬性-路徑操作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')
In [5]: p.drive, p2.drive # Unix-style的路徑沒有盤的概念
Out[5]: ('', 'c:')
In [6]: p.root, p2.root
Out[6]: ('/', '\\')
In [7]: p1.anchor, p2.anchor # anchor 就是指 drive和root兩者
Out[7]: ('/', 'c:\\')
該屬性是路徑的所有父目錄,返回一個Iterable對象[3]
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
/
該屬性指路徑的父目錄,p.parent則是/Users/jeffery
name該屬性指路徑最後一層級的名稱,但不包括drive和root,可方便的用於取文件名或文件夾名
In [13]: p1.name
Out[13]: 'a.b.c.txt'
In [14]: PurePath('/').name
Out[14]: ''
In [15]: p.suffix # 取文件擴展名
Out[15]: ''
In [16]: p1.suffix
Out[16]: '.txt'
In [17]: p1.suffixes # 取多層級的文件擴展名,其本質就是根據.進行字符串分割
Out[17]: ['.b', '.c', '.txt']
相當於先取path.name,然後再去除文件擴展名(path.suffix)
In [18]: p1.stem
Out[18]: 'a.b.c'
In [19]: p = PureWindowsPath('c:\\windows')
In [20]: str(p)
Out[20]: 'c:\\windows'
In [21]: p.as_posix()
Out[21]: 'c:/windows'
將路徑轉換成文件url的形式,參數path必須是絕對路徑,否則拋出ValueError
In [22]: p1.as_uri()
Out[22]: 'file:///Users/jeffery/a.b.c.txt'
判斷一個路徑是否是絕對路徑的形式
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
判斷一個目錄是否是作業系統中一些特殊的文件設備名,如nul, com1, com2等,該命令只針對windows-like path, Posix-like path總是返回False.
In [35]: PureWindowsPath('nul').is_reserved()
Out[35]: True
路徑名連接,等效於操作符/
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')
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
以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]: '.'
該方法將路徑中原本的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'
該方法將路徑中原本的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'
該方法將路徑中原本的suffix替換為新的suffix,如果suffix="",則原本的suffix直接移除
In [54]: p1.with_suffix('.py')
Out[54]: PurePosixPath('/Users/jeffery/a.b.c.py')
為了探索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')
該方法返回當前所在文件夾,等同於os.getcwd(),因此可以知道該方法應該是一個classmethod[4],其調用與具體的對象無關
In [4]: Path.cwd(), PosixPath.home()
Out[4]: (PosixPath('/Users/jeffery'), PosixPath('/Users/jeffery'))
該方法也是一個classmethod[5],返回用戶的家目錄路徑,等同於os.path.expanduser()
stat(*, follow_symlinks=True)該方法類似於前面介紹的os.stat(),返回一個os.stat_result對象(stat_result詳細屬性請見os.stat_result[6])。所以,類似的,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)
修改路徑權限,類似於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
判斷路徑是否存在,值得注意的是,當路徑是連結文件時,該方法會進一步追蹤查看對應的符號連結是否存在
expanduser()同上面的os.path.expanduser(),如果家目錄無法確定的話,則拋出RuntimeError
glob(pattern)根據pattern搜索路徑中匹配的文件,pattern的匹配規則同fnmatch[7],主要利用以下unix shell-like通配符進行簡單的文件匹配任務(如更複雜的任務,請參考python re 正則表達式[8]):
PatternMeaning*matches everything?matches any single character[seq]matches any character in seq[!seq]matches any character not in seqglob(pattern)方法返回一個generator[9]類型,可以通過迭代的方法取出所有匹配的文件:
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[10]中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']
判斷路徑是否是文件夾,返回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]
不同於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()
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_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')
重命名文件,返回值為更新後指向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'
同上,重命名文件,返回值為更新後指向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'
該方法等同於Path.glob("**/{pattern}")
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
類似於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
創建文件,當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_bytes,但是以text形式寫入文件
對照表python documentation官方網站也給出了pathlib庫與os及os.path模塊實現相同功能函數的對照表如下,如果你已經熟悉後者,可以依據該表格快速的進行」遷移學習「。
os and os.pathpathlibos.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() 2os.stat()Path.stat(), Path.owner(), Path.group()os.path.isabs()PurePath.is_absolute()os.path.join()PurePath.joinpath()os.path.basename()PurePath.nameos.path.dirname()PurePath.parentos.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[11]中便是採用了pathlib庫進行路徑處理工作,是項目開發中不可多得的好例子。
Reference[1]Python字符串處理:https://blog.csdn.net/jeffery0207/article/details/79477629
[2]os.stat_result:https://docs.python.org/3/library/os.html#os.stat_result
[3]Iterable對象:https://blog.csdn.net/jeffery0207/article/details/88112825
[4]classmethod:https://blog.csdn.net/jeffery0207/article/details/89042654
[5]classmethod:https://blog.csdn.net/jeffery0207/article/details/89042654
[6]os.stat_result:https://docs.python.org/3/library/os.html#os.stat_result
[7]fnmatch:https://docs.python.org/3/library/fnmatch.html#module-fnmatch
[8]python re 正則表達式:https://blog.csdn.net/jeffery0207/article/details/81806714
[9]generator:https://blog.csdn.net/jeffery0207/article/details/88112825
[10]glob:https://docs.python.org/3/library/glob.html
[11]scanpy:https://github.com/theislab/scanpy
[12]python pathlib:https://docs.python.org/3/library/pathlib.html
[13]python stat:https://docs.python.org/3/library/stat.html
[14]get unix permission mask:https://stackoverflow.com/questions/5337070/how-can-i-get-the-unix-permission-mask-from-a-file