pathlib

  • 轉(zhuǎn)自知乎
    之前如果要使用 python 操作文件路徑缤苫,我總是會(huì)條件反射導(dǎo)入 os.path。 而現(xiàn)在窄俏,我會(huì)更加喜歡用新式的 pathlib, 雖然用得還是沒有 os.path 熟練霸旗,但是以后會(huì)堅(jiān)持使用。

pathlib 庫(kù)從 python3.4 開始敢伸,到 python3.6 已經(jīng)比較成熟犬缨。如果你的新項(xiàng)目可以直接用 3.6 以上喳魏,建議用 pathlib。相比于老式的 os.path 有幾個(gè)優(yōu)勢(shì):

  1. 老的路徑操作函數(shù)管理比較混亂怀薛,有的是導(dǎo)入 os, 有的又是在 os.path 當(dāng)中刺彩,而新的用法統(tǒng)一可以用 pathlib 管理。
  2. 老用法在處理不同操作系統(tǒng) win枝恋,mac 以及 linux 之間很吃力创倔。換了操作系統(tǒng)常常要改代碼,還經(jīng)常需要進(jìn)行一些額外操作焚碌。
  3. 老用法主要是函數(shù)形式畦攘,返回的數(shù)據(jù)類型通常是字符串。但是路徑和字符串并不等價(jià)十电,所以在使用 os 操作路徑的時(shí)候常常還要引入其他類庫(kù)協(xié)助操作知押。新用法是面向?qū)ο螅幚砥饋?lái)更靈活方便鹃骂。
  4. pathlib 簡(jiǎn)化了很多操作台盯,用起來(lái)更輕松。

舉個(gè)例子偎漫, 把所有的 txt 文本全部移動(dòng)到 archive 目錄當(dāng)中(archive 目錄必須存在)爷恳。

[圖片上傳失敗...(image-5ae50d-1678255402118)]

使用原來(lái)的用法:

import glob
import os
import shutil

# 獲取運(yùn)行目錄下所有的 txt 文件。注意:不是這個(gè)文件目錄下
print(glob.glob('*.txt'))

for file_name in glob.glob('*.txt'):
    new_path = os.path.join('archive', file_name)
    shutil.move(file_name, new_path)

新的寫法:

from pathlib import Path

Path("demo.txt").replace('archive/demo.txt')

這篇文章列舉了 pathlib 的主要用法象踊,細(xì)節(jié)知識(shí)有點(diǎn)多,我只會(huì)介紹 pathlib 的用法棚壁,不會(huì)每個(gè)都列舉和 os 的區(qū)別杯矩。但是我會(huì)在文章最后放上主要操作使用 pathlib 和 os 的對(duì)比圖。

最主要的路徑操作袖外,你可以參考國(guó)外一位大神總結(jié)的圖:

[圖片上傳失敗...(image-b8d725-1678255402118)]

1 路徑獲取

  • 獲取當(dāng)前工作目錄
>>> import pathlib
>>> pathlib.Path.cwd()
C:\Users\me\study
# WindowsPath('C:\Users\me\study')

雖然在這里打印出來(lái)的很像一個(gè)字符串史隆,但實(shí)際上得到的是一個(gè) WindowsPath('C:\Users\me\study')對(duì)象。顯示內(nèi)容由 Path 類的 __repr__ 定義曼验。

注意
工作目錄是在哪個(gè)目錄下運(yùn)行你的程序泌射,不是項(xiàng)目目錄粘姜。

如果你只想得到字符串表示,不想要 WindowsPath 對(duì)象熔酷,可以用 str() 轉(zhuǎn)化:

>>> str(pathlib.Path.cwd())
C:\Users\me\study
  • 獲取用戶 home 目錄孤紧。

下面的例子因?yàn)榛径际鞘褂?pathlib 下面的 Path 類,所以可以換一種導(dǎo)入方式拒秘。

from pathlib import Path
>>> Path.home()
c:\Users\me
  • 獲取當(dāng)前文件路徑
>>> Path(__file__)
demo_01.py

在 pycharm 中右擊運(yùn)行和在 cmd 運(yùn)行的結(jié)果會(huì)不同号显。pycharm 會(huì)顯示全路徑,cmd 運(yùn)行只會(huì)顯示工作目錄下的相對(duì)路徑躺酒。如果想統(tǒng)一押蚤,可以添加后綴 .resolve() 轉(zhuǎn)化成絕對(duì)路徑,這個(gè)在后面還會(huì)提到羹应。

[圖片上傳失敗...(image-51b151-1678255402118)]

  • 獲取任意字符串路徑
>>> Path('subdir/demo_02.py')
subdir\demo_02.py
>>> Path('c:d:y/rad.txt')
c:d:y\rad.txt

這里需要注意 2 點(diǎn):

  1. 不管字符串使用的是正斜杠 / 還是反斜杠 \揽碘, 在 windows 系統(tǒng)里,得到的路徑都是反斜杠\, pathlib 會(huì)根據(jù)操作系統(tǒng)智能處理园匹。
  2. 第二個(gè)例子中字符串會(huì)被 / 分割雳刺,c:d:y 會(huì)被當(dāng)做一個(gè)目錄名字,pathlib 不會(huì)去判斷這個(gè)文件真的存在哦偎肃。
  • 獲取絕對(duì)路徑

只需要在任意路徑對(duì)象后添加方法 .resolve() 就能獲取路徑的絕對(duì)路徑煞烫。如果填入的路徑是相對(duì)路徑(windows 下沒有盤符,linux 沒有 / 開頭)累颂,則會(huì)在當(dāng)前工作目錄后添加路徑滞详。如果是已經(jīng)是絕對(duì)路徑,則只會(huì)根據(jù)操作系統(tǒng)優(yōu)化表達(dá)紊馏。

>>> file = Path('archive/demo.txt')
>>> file
archive\demo.txt
>>> file.resolve()
C:\Users\me\study\archive\demo.txt
  • 獲取文件屬性

文件屬性比如文件大小料饥,創(chuàng)建時(shí)間,修改時(shí)間等等朱监。

file = Path('archive/demo.txt')
print(file.stat())
print(file.stat().st_size)
print(file.stat().st_atime)
print(file.stat().st_ctime)
print(file.stat().st_mtime)

找出最后修改的文件的例子:

>>> path = Path.cwd()
>>> max(
        [(f.stat().st_mtime, f) 
         for f in path.iterdir() 
         if f.is_file()]
    )
1589171135.860173 C:\Users\me\study\demo_03.py

2 路徑組成部分

獲取路徑的組成部分非常方便:

  • .name 文件名岸啡,包含后綴名,如果是目錄則獲取目錄名赫编。
  • .stem 文件名巡蘸,不包含后綴。
  • .suffix 后綴擂送,比如 .txt, .png悦荒。
  • .parent 父級(jí)目錄,相當(dāng)于 cd ..
  • .anchor 錨嘹吨,目錄前面的部分 C:\ 或者 /搬味。
>>> file = Path('archive/demo.txt')
>>> file.name
demo.txt

>>> file.stem
demo

>>> file.suffix
.txt

>>> file.parent
C:\Users\me\study\archive

>>> file.anchor
'C:\'
  • 獲取上一級(jí)目錄
>>> file = Path('archive/demo.txt')
>>> file.parent
archive

獲取上 2 級(jí) 和 3 級(jí)目錄會(huì)有點(diǎn)問題。因?yàn)閭魅氲氖且粋€(gè)相對(duì)路徑,上 3 級(jí)已經(jīng)無(wú)法往上了碰纬,所以還是會(huì)停留在工作目錄萍聊。所以你需要確保你的路徑有這么多層級(jí)。

>>> file.parent.parent
.
>>> file.parent.parent.parent
.
  • 獲取所有的上級(jí)目錄:
>>> file.parents
<WindowsPath.parents>
>>> list(file.parents)
[WindowsPath('archive'), WindowsPath('.')]
  • 父級(jí)目錄的另一種表示方法:
>>> file.parents[0]
archive

如果路徑是在當(dāng)前工作目錄下的子目錄悦析,最好轉(zhuǎn)化成絕對(duì)路徑再獲取上層目錄:

>>> file.resolve().parents[4]
C:\Users
# 不能使用負(fù)數(shù)

在 os 模塊中獲取上 3 級(jí)目錄簡(jiǎn)直令人奔潰寿桨,需要重復(fù)使用 dirname 函數(shù),使用 pathlib 的 parent 可以極大簡(jiǎn)單化操作:

>>> import os
>>> os.path.dirname(file)
  • 相對(duì)其他某個(gè)路徑的結(jié)果:
>>> file.relative_to('archive')
dmeo.txt

3 子路徑掃描

  • dir_path.iterdir() 可以掃描某個(gè)目錄下的所有路徑(文件和子目錄)她按, 打印的會(huì)是處理過(guò)的絕對(duì)路徑牛隅。
>>> cwd = Path.cwd()
>>> [path for path in cwd.iterdir() if cwd.is_dir()]
[
    WindowsPath('C:/Users/me/study/archive'), 
    WindowsPath('C:/Users/me/study/demo_01.py'), 
    WindowsPath('C:/Users/me/study/new_archive')
]
  • 使用 iterdir() 可以統(tǒng)計(jì)目錄下的不同文件類型:
>>> path = Path.cwd()
>>> files = [f.suffix for f in path.iterdir() if f.is_file()]
>>> collections.Counter(files)
Counter({'.py': 3, '.txt': 1})
  • 查找目錄下的指定文件 glob 。

使用模式匹配(正則表達(dá)式)匹配指定的路徑酌泰。正則表達(dá)式不熟練的可以查看 這個(gè)教程媒佣,真的讓我學(xué)會(huì)了正則表達(dá)式。glob 只會(huì)匹配當(dāng)前目錄下陵刹, rglob 會(huì)遞歸所有子目錄默伍。下面這個(gè)例子,demo.txt 在 archive 子目錄下衰琐。所以用 glob 找到的是空列表也糊,rglob 可以找到。glob 得到的是一個(gè)生成器羡宙,可以通過(guò) list() 轉(zhuǎn)化成列表狸剃。

rglob理論上是可以用正則,但是并不好用狗热。比如要同時(shí)搜索xlsx和csv钞馁,你只能寫成[xv],不能用(xlsx|csv)匿刮,也可能是沒有寫正確僧凰。

>>> cwd = Path.cwd()
>>> list(cwd.glob('*.txt'))
[]
>>> list(cwd.rglob('*.txt'))
[WindowsPath('C:/Users/me/study/archive/demo.txt')]
  • 檢查路徑是否符合規(guī)則 match
>>> file = Path('/archive/demo.txt')
>>> file.match('*.txt')
True

4 路徑拼接

pathlib 支持用 / 拼接路徑。熟悉魔術(shù)方法的同學(xué)應(yīng)該很容易理解其中的原理熟丸。

>>> Path.home() / 'dir' / 'file.txt'
C:\Users\me\dir\file.txt

如果用不慣 / 训措,也可以用類似 os.path.join 的方法:

>>> Path.home().joinpath('dir', 'file.txt')
C:\Users\me\dir\file.txt

5 路徑測(cè)試(判斷)

  • 是否為文件
>>> Path("archive/demo.txt").is_file()
True
>>> Path("archive/demo.txt").parent.is_file()
False
  • 是否為文件夾 (目錄)
>>> Path("archive/demo.txt").is_dir()
False
>>> Path("archive/demo.txt").parent.is_dir()
True
  • 是否存在
>>> Path("archive/demo.txt").exists
True
>>> Path("archive/dem.txt").exists
False

6 文件操作

  • 創(chuàng)建文件 touch
>>> file = Path('hello.txt')
>>> file.touch(exist_ok=True)
None

>>> file.touch(exist_ok=False)
FileExistsError: [Errno 17] File exists: 'hello.txt'

exist_ok 表示當(dāng)文件已經(jīng)存在時(shí),程序的反應(yīng)光羞。如果為 True绩鸣,文件存在時(shí),不進(jìn)行任何操作纱兑。如果為 False, 則會(huì)報(bào) FileExistsError 錯(cuò)誤全闷。

  • 創(chuàng)建目錄 path.mkdir

用 os 創(chuàng)建目錄分為 2 個(gè)函數(shù) : mkdir()makedirs()mkdir() 一次只能創(chuàng)建一級(jí)目錄萍启, makedirs() 可以同時(shí)創(chuàng)建多級(jí)目錄:

>>> os.mkdir('dir/subdir/3dir')
FileNotFoundError: [WinError 3] 系統(tǒng)找不到指定的路徑。: 'dir/subdir/3dir'

>>> os.makedirs('dir/subdir/3dir')
None

使用 pathlib 只需要用 path.mkdir() 函數(shù)就可以。它提供了 parents 參數(shù)勘纯,設(shè)置為 True 可以創(chuàng)建多級(jí)目錄局服;不設(shè)置則只能創(chuàng)建 一層:

>>> path = Path('/dir/subdir/3dir')
>>> path.mkdir()
FileNotFoundError: [WinError 3] 系統(tǒng)找不到指定的路徑。: 'dir/subdir/3dir'

>>> path.mkdir(parents=True)
None
  • 刪除目錄 path.rmdir()

刪除目錄非常危險(xiǎn)驳遵,并且沒有提示淫奔,一定要謹(jǐn)慎操作。一次只刪除一級(jí)目錄堤结,且當(dāng)前目錄必須為空唆迁。

>>> path = Path('dir/subdir/3dir')
>>> path.rmdir()
None
  • 刪除文件 path.unlink, 危險(xiǎn)操作。
>>> path = Path('archive/demo.txt')
>>> path.unlink()
  • 打開文件

使用 open() 函數(shù)打開文件時(shí)竞穷,如果需要傳入文件路徑唐责。可以用字符串作為參數(shù)傳入:

with open('archive/demo.txt') as f:
    print(f.read())

也可以傳入 Path 對(duì)象:

file_path = Path('archive/demo.txt')
with open(file_path) as f:
    print(f.read())

如果經(jīng)常使用 pathlib瘾带,可以在獲取到 Path 路徑以后直接調(diào)用 path.open() 方法鼠哥。至于到底用哪一個(gè),其實(shí)不必太在意看政,因?yàn)?path.open() 也是調(diào)用內(nèi)置函數(shù) open()朴恳。

file = Path('archive/demo.txt')
with file.open() as f:
    print(f.read())

不過(guò) pathlib 對(duì)讀取和寫入進(jìn)行了簡(jiǎn)單的封裝,不再需要重復(fù)去打開文件和管理文件的關(guān)閉了允蚣。

  • .read_text() 讀取文本
  • .read_bytes() 讀取 bytes
  • .write_text() 寫入文本
  • .write_bytes() 寫入 tytes
>>> file_path = Path('archive/demo.txt')

>>> file_path.read_text()   # 讀取文本
'text in the demo.txt'

>>> file_path.read_bytes()  # 讀取 bytes
b'text in the demo.txt'

>>> file.write_text('new words')  # 寫入文本
9

>>> file.write_bytes(b'new words') # 寫入 bytes
9

注意
file.write 操作使用的是 w 模式于颖,如果之前已經(jīng)有文件內(nèi)容,將會(huì)被覆蓋嚷兔。

  • 移動(dòng)文件
txt_path = Path('archive/demo.txt')
res = txt_path.replace('new_demo.txt')
print(res)

這個(gè)操作會(huì)把 archive 目錄下的 demo.txt 文件移動(dòng)到當(dāng)前工作目錄森渐,并重命名為 new_demo.txt。

移動(dòng)操作支持的功能很受限谴垫。比如當(dāng)前工作目錄如果已經(jīng)有一個(gè) new_demo.txt 的文件章母,則里面的內(nèi)容都會(huì)被覆蓋。還有翩剪,如果需要移動(dòng)到其他目錄下乳怎,則該目錄必須要存在,否則會(huì)報(bào)錯(cuò):

# new_archive 目錄必須存在前弯,否則會(huì)報(bào)錯(cuò)
txt_path = Path('archive/demo.txt')
res = txt_path.replace('new_archive/new_demo.txt')
print(res)

為了避免出現(xiàn)同名文件里的內(nèi)容被覆蓋蚪缀,通常需要進(jìn)行額外處理。比如判斷同名文件不能存在恕出,但是父級(jí)目錄必須存在询枚;或者判斷父級(jí)目錄不存在時(shí),創(chuàng)建該目錄浙巫。

dest = Path('new_demo.txt')
if (not dest.exists()) and dest.parent.exists():
 txt_path.replace(dest)
  • 重命名文件
txt_path = Path('archive/demo.txt')
new_file = txt_path.with_name('new.txt')
txt_path.replace(new_file)
  • 修改后綴名
txt_path = Path('archive/demo.txt')
new_file = txt_path.with_suffix('.json')
txt_path.replace(new_file)

注意
不管是移動(dòng)文件還是刪除文件金蜀,都不會(huì)給任何提示刷后。所以在進(jìn)行此類操作的時(shí)候要特別小心。
對(duì)文件進(jìn)行操作最好還是用 shutil 模塊渊抄。

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尝胆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子护桦,更是在濱河造成了極大的恐慌含衔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件二庵,死亡現(xiàn)場(chǎng)離奇詭異贪染,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)催享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門杭隙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人睡陪,你說(shuō)我怎么就攤上這事寺渗。” “怎么了兰迫?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵信殊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我汁果,道長(zhǎng)涡拘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任据德,我火速辦了婚禮鳄乏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棘利。我一直安慰自己橱野,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布善玫。 她就那樣靜靜地躺著水援,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茅郎。 梳的紋絲不亂的頭發(fā)上蜗元,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音系冗,去河邊找鬼奕扣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掌敬,可吹牛的內(nèi)容都是我干的惯豆。 我是一名探鬼主播池磁,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼循帐!你這毒婦竟也來(lái)了框仔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拄养,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后银舱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘪匿,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年寻馏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棋弥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诚欠,死狀恐怖顽染,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轰绵,我是刑警寧澤粉寞,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站左腔,受9級(jí)特大地震影響唧垦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜液样,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一振亮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞭莽,春花似錦坊秸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至丹拯,卻和暖如春站超,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乖酬。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工死相, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咬像。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓算撮,卻偏偏與公主長(zhǎng)得像生宛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肮柜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容