1. 程式人生 > >【轉】Python 3的pathlib模塊:馴服文件系統

【轉】Python 3的pathlib模塊:馴服文件系統

重載 文件名 代碼 找文件 完全 d3d 是否 glob 測試

【轉】Python 3的pathlib模塊:馴服文件系統

https://python.freelycode.com/contribution/detail/1248

Python部落(python.freelycode.com)組織翻譯,禁止轉載,歡迎轉發。

目錄

  • python文件路徑處理時的問題

  • 創建路徑

  • 讀寫文件

  • 找出路徑的組成部分

  • 移動和刪除文件

  • 例子

    • 計算文件數量

    • 顯示目錄樹

    • 查找上次修改的文件

    • 創建唯一的文件名

  • 操作系統的差別

  • 以合適的對象表現路徑

  • 結論

你是否遇到過使用python處理文件路徑時的麻煩呢?在python 3.4以及更高版本中,這些麻煩不復存在。不再需要像下面這樣從頭開始編寫代碼:

技術分享圖片

或者編寫下面這樣冗長煩人的代碼:

技術分享圖片

在這篇文章中,你將看到怎樣使用python處理文件路徑-目錄和文件的名字。

技術分享圖片

python文件路徑處理時的問題

處理文件和與文件系統交互很重要,原因很多。 最簡單的情況可能只涉及讀取或寫入文件,但有時會是更復雜的任務。也許您需要列出目錄中屬於給定類型的的所有文件,查找給定文件的父目錄,或者創建一個不存在的唯一文件名。

習慣上,Python使用常規文本字符串表示文件路徑。 在os.path標準庫的支持下,盡管有點麻煩,但這已經足夠了(正如引言中的第二個例子所示)。然而,由於路徑不是字符串,重要的功能遍布在標準庫中,包括像os,glob和shutil這樣的庫。 下面的示例需要三個導入語句,以便將所有文本文件移動到歸檔目錄:

技術分享圖片

對於由字符串表示的路徑,使用常規字符串方法是可能的,但通常是一個壞主意。 例如,你應該使用os.path.join()來鏈接兩條路徑,它使用操作系統上正確的路徑分隔符來鏈接路徑,而不是像普通的字符串那樣使用“+”。 回想一下Windows使用\,而Mac和Linux使用/作為分隔符。 這種差異可能會導致難以發現的錯誤,例如我們介紹的第一個示例僅適用於Windows路徑。

Python 3.4中引入了pathlib模塊(PEP 428)來處理這些挑戰。 它在一個地方收集必要的功能,並通過易於使用的Path對象上的方法和屬性使其可用。

早期,其他軟件包仍然使用字符串作為文件路徑,但從Python 3.6開始,pathlib模塊在整個標準庫中得到支持,部分原因是由於增加了文件系統路徑協議。 如果你堅持使用傳統的Python,那麽Python 2也有一個可用的向後移植。

實踐時間:讓我們看看pathlib如何在實踐中發揮作用。

創建路徑

你真正需要知道的是pathlib.Path類。 創建路徑有幾種不同的方式。 首先,有類方法,如.cwd()(當前工作目錄)和.home()(用戶的主目錄):

技術分享圖片

註意:在本教程中,我們將假定已經導入了pathlib,而沒有像上面那樣給出import pathlib。 由於您將主要使用Path類,因此您也可以使用語句from pathlib import Path並使用Path而不是pathlib.Path。

還可以從其字符串表示中顯式創建路徑:

技術分享圖片

處理Windows路徑的小技巧:在Windows上,路徑分隔符是反斜杠\。 但是,在許多情況下,反斜杠也用作轉義字符以表示不可打印的字符。 為避免出現問題,請使用原始字符串來表示Windows路徑。 這些字符串,它們前面都有一個r。 在原始字符串中,\表示文字反斜線:r‘C:\Users‘。

構建路徑的第三種方法是使用特殊運算符“/”鏈接路徑的各個部分。 正斜杠操作符獨立於平臺上的實際路徑分隔符使用:

技術分享圖片

只要至少有一個Path對象,/就可以連接多個路徑或路徑和字符串的混合(如上所述)。 如果你不喜歡特殊/符號,你可以用.joinpath()方法做同樣的事情:

技術分享圖片

請註意,在前面的示例中,pathlib.Path由WindowsPath或PosixPath表示。 表示路徑的實際對象取決於底層操作系統。 (也就是說,在Windows上結果表現為一個WindowsPath對象,而在Mac或Linux上結果表現為一個PosixPath對象。)有關更多信息,請參閱操作系統差異部分。

讀寫文件

習慣上,使用Python讀取或寫入文件的方法是使用內置的open()函數。 這仍然是正確的,因為open()函數可以直接使用Path對象。 以下示例查找Markdown文件中的所有標題並打印它們:

技術分享圖片

一個等同的選擇是在Path對象上調用.open():

技術分享圖片

實際上,Path.open()在後臺調用內置的open()。 你使用哪個選項主要是品味的問題。

對於簡單的文件讀寫,在pathlib庫中有幾個簡便的方法:

  • .read_text(): 以文本模式打開路徑並並以字符串形式返回內容。

  • .read_bytes(): 以二進制/字節模式打開路徑並以字節串的形式返回內容。

  • .write_text(): 打開路徑並向其寫入字符串數據。

  • .write_bytes(): 以二進制/字節模式打開路徑並向其寫入數據。

這些方法中的每一個都能處理文件的打開和關閉,使用起來非常簡便,例如:

技術分享圖片

路徑也可以指定為簡單的文件名,在這種情況下,它們是相對於當前工作目錄進行解析的。 以下示例等同於上一個示例:

技術分享圖片

.resolve()方法將找到完整路徑。 下面,我們確認當前工作目錄用於簡單文件名:

技術分享圖片

請註意,比較路徑時,比較的是它們的表示方式。 在上面的例子中,path.parent不等於pathlib.Path.cwd(),因為path.parent用‘.‘表示。 而pathlib.Path.cwd()由‘/home/gahjelle/realpython/‘表示。

找出路徑的組成部分

路徑的不同部分可以方便地用作屬性。 基本的例子包括:

  • .name: 沒有任何目錄的文件名

  • .parent: 包含該文件的目錄,或者如果path是目錄,則是父目錄

  • .stem: 文件名不帶後綴

  • .suffix: 文件擴展名

  • .anchor: 目錄之前的路徑部分

以下是這些屬性的作用:

技術分享圖片

請註意.parent返回一個新的Path對象,而其他屬性返回字符串。 這意味著,例如,可以像上一個示例那樣鏈接.parent,甚至可以與/組合創建完全新的路徑:

技術分享圖片

優秀的Pathlib備忘錄提供了所有屬性和方法的可視化表示。

移動和刪除文件

通過pathlib,您還可以訪問基本的文件系統級操作,如移動,更新甚至刪除文件。 大多數情況下,這些方法不會在信息或文件丟失之前發出警告或等待確認。 使用這些方法時要小心。

要移動文件,請使用.replace()。 請註意,如果目標已存在,.replace()將覆蓋它。 不幸的是,pathlib沒有明確支持安全移動文件。 為了避免可能覆蓋目標路徑,最簡單的方法是在更換之前測試目標是否存在:

技術分享圖片

然而,這確實為可能的資源競爭留下隱患。 另一個進程可能會在執行if語句和.replace()方法之間的目標路徑中添加一個文件。 如果這是一個問題,更安全的方法是打開獨占創建的目標路徑並明確復制源數據:

技術分享圖片

如果目標已經存在,上面的代碼將引發FileExistsError。 從技術上講,這復制了一個文件。 要執行移動,只需在完成復制後刪除源(請參閱下文)。 確保沒有發生異常。

重命名文件時,有用的方法可能是.with_name()和.with_suffix()。 它們都返回原始路徑,但分別替換名稱或後綴。

例如:

技術分享圖片

目錄和文件可分別使用.rmdir()和.unlink()刪除。 (再次提醒,要非常小心!)

例子

在本節中,您將看到一些如何使用pathlib來處理簡單挑戰的示例。

計算文件數量

列出大量文件有幾種不同的方法。 最簡單的是.iterdir()方法,它遍歷給定目錄中的所有文件。 以下示例將.iterdir()與collections.Counter類組合起來,以統計當前目錄中每個文件類型的文件數量:

技術分享圖片

使用方法.glob()和.rglob()(遞歸glob)可以創建更靈活的文件列表。 例如,pathlib.Path.cwd()。glob(‘*.txt‘)返回當前目錄中所有帶有.txt後綴的文件。 以下僅計算以p開頭的文件類型:

技術分享圖片

顯示目錄樹

下一個示例定義了一個函數tree(),它將打印一個表示文件層次結構的可視化樹,該樹以根目錄為根。 在這裏,我們也想列出子目錄,所以我們使用.rglob()方法:

技術分享圖片

請註意,我們需要知道文件所在的目錄到根目錄有多遠。 為此,我們首先使用.relative_to()來表示相對於根目錄的路徑。 然後,我們計算表示中的目錄數量(使用.parts屬性)。 運行時,此函數創建如下的可視化樹:

技術分享圖片

  • 註意:f-strings只能在Python 3.6及更高版本中使用。 在較老的Pythons中,表達式f‘{spacer} + {path.name}‘可以寫為‘{0} + {1}‘.format(spacer,path.name)。

查找上次修改的文件

.iterdir(),.glob()和.rglob()方法非常適合生成器表達式和列表解析。 要在上次修改的目錄中查找文件,可以使用.stat()方法獲取有關底層文件的信息。 例如,.stat().st_mtime給出了文件上次修改的時間:

技術分享圖片

您甚至可以使用類似的表達式獲取上次修改的文件的內容:

技術分享圖片

從不同的.stat().st_屬性返回的時間戳表示1970年1月1日以來的秒數。除了datetime.fromtimestamp之外,time.localtime或time.ctime可用於將時間戳轉換為更加可用的內容。

創建一個唯一的文件名

最後一個例子將顯示如何基於模板構建唯一編號的文件名。 首先,為文件名指定一個模式,並為計數器留出空間。 然後,檢查通過加入目錄和文件名創建的文件路徑是否存在(使用計數器的值)。 如果它已經存在,請增加計數器並重試:

技術分享圖片

如果該目錄已經包含文件test001.txt和test002.txt,則上述代碼將設置路徑為test003.txt。

操作系統差異

早些時候,我們註意到當我們實例化pathlib.Path時,返回了WindowsPath或PosixPath對象。 對象的種類取決於您使用的操作系統。 該功能使編寫跨平臺兼容代碼變得相當容易。 可以明確地詢問WindowsPath或PosixPath,但您只會將代碼限制在該系統中,沒有任何好處。 這樣的具體路徑不能用於不同的系統:

技術分享圖片

有時可能需要表示路徑而不訪問底層文件系統(在這種情況下,在非Windows系統上表示Windows路徑也是有意義的,反之亦然)。 這可以使用PurePath對象完成。 這些對象支持路徑組件部分討論的操作,但不支持訪問文件系統的方法:

技術分享圖片

您可以直接在所有系統上實例化PureWindowsPath或PurePosixPath。 根據您使用的操作系統,實例化PurePath將返回其中一個對象。

作為合適對象的路徑

在簡介中,我們簡單地指出路徑不是字符串,pathlib背後的一個動機是用適當的對象表示文件系統。 事實上,pathlib的官方文檔標題為pathlib--面向對象的文件系統路徑。 在上面的例子中,面向對象的方法已經非常明顯(特別是如果你將它與舊的os.path方式做對比)。 但是,讓我給你留下一些其他小知識。

與正在使用的操作系統無關,路徑以Posix樣式表示,正斜杠作為路徑分隔符。 在Windows上,您將看到如下所示的內容:

技術分享圖片

但是,當路徑轉換為字符串時,它將使用本地形式,例如在Windows上使用反斜杠:

技術分享圖片

如果您正在使用不知道如何處理pathlib.Path對象的庫,這特別有用。 這是3.6之前的Python版本中的一個大問題。 例如,在Python 3.5中,configparser標準庫只能使用字符串路徑來讀取文件。 處理這種情況的方法是明確地轉換為字符串:

技術分享圖片

在Python 3.6及更高版本中,如果需要進行顯式轉換,建議使用os.fspath()而不是str()。 這樣會更安全一些,因為如果您偶然嘗試轉換不是路徑的對象,它會引發錯誤。

可能是pathlib庫最不尋常的部分是使用/運算符。 窺探一下源碼,讓我們看看它是如何實現的。 這是運算符重載的一個例子:運算符的行為根據上下文而改變。 你以前見過這個。 想想+對於字符串和數字來說意味著不同的東西。 Python通過使用雙下劃線方法(a.k.a. dunder方法)來實現運算符重載。

/運算符由.__ truediv __()方法定義。 事實上,如果你看看pathlib的源代碼,你會看到類似於:

技術分享圖片

結論

自Python 3.4以來,pathlib已經在標準庫中可用。 使用pathlib,文件路徑可以用適當的Path對象來表示,而不像以前那樣用普通的字符串表示。 這些對象使代碼處理文件路徑:

  • 易於閱讀,尤其是因為/用於將路徑連接在一起

  • 功能更強大,可直接在對象上使用最必要的方法和屬性

  • 在操作系統中更一致,因為不同系統的特性被Path對象隱藏

英文原文:https://realpython.com/python-pathlib/
譯者:javylee

【轉】Python 3的pathlib模塊:馴服文件系統