- 如果你用 Mac 或 Linux 作業系統,你的檔案路徑會使用前斜線(/)
- 如果位在 Windows 作業系統,則是反斜線(\)。
那麼,當你想寫個在不同作業系統都能操作檔案路徑的 Python 程式時,該怎麼辦?
下方是一個以字串處理來操作路徑的 Python 範例,需要根據作業系統使用不同的字串處理方法,不太有效率:
# 範例:查詢檔案所在資料夾的路徑 ## Windows 是後斜線 >>> 'C:\Users\haohao\test.txt'.rsplit('\\', maxsplit=1) ['C:\\Users\\haohao', 'test.txt'] ## Mac 或 Linux 是前斜線 >>> '/Users/haohao/test.txt'.rsplit('/', maxsplit=1) ['/Users/haohao', 'test.txt']
在 Python 3.4 版本之後,將 pathlib 納入了標準函式庫,使用 pathlib 內的 Path 類別就能讓你做到方便的檔案路徑操作、簡易的檔案讀寫、並且幫你處理好上面範例的跨作業系統問題!
好豪將在這篇筆記用幾項簡單範例說明 pathlib 的威力,讓你可以立即上手,並且介紹深度學習套件 fastai 如何運用 pathlib 來提升工作效率。
目錄
pathlib 的 Path 類別介紹
物件導向的檔案路徑操作
在 pathlib 將檔案路徑透過 Path 類別包裝之後,就可以使用各種方便的檔案路徑操作。
## 大多數常用操作,只需要用到 Path 這個類別 >>> from pathlib import Path ## 引數傳入你要指向的位置,此例指向桌面 >>> p = Path('./Desktop') >>> p PosixPath('Desktop') ## resolve() 找出絕對路徑 >>> p.resolve() PosixPath('/Users/haohao/Desktop') ## 若沒有傳入引數,預設指向開啟 Python 的位置 >>> p = Path() >>> p PosixPath('.') >>> p.resolve() PosixPath('/Users/haohao/Desktop')
跨作業系統
若作業系統不同,Path 會幫你以不同方式處理檔案路徑。
- 不論哪個作業系統,在 Path 物件內部,一律用前斜線(/)儲存路徑資料
- 然而,當你用 print() 或是 str() 來表示 Path 的路徑字串,Path 物件會幫你依據程式執行所在的作業系統決定相對應輸出字串。
## Windows 執行結果 -> 建立 WindowsPath >>> p = Path() >>> p WindowsPath('.') ## 不論任何作業系統,內部儲存的路徑表示是前斜線 >>> p.resolve() WindowsPath('C:/Users/haohao/') ## print() 或 str() 會根據執行的作業系統決定 ## 此處 print() 結果是 Windows 所用的後斜線(\) >>> print(p.resolve()) C:\Users\haohao\ ## 使用 str() 時,後斜線會用跳脫字元表示 >>> str(p.resolve()) 'C:\\Users\\haohao\\' ## 就算用後斜線建立 Path 物件,Path 內部儲存路徑依然是用前斜線 ## 後斜線記得要跳脫(\\) >>> p = Path('C:\\Users\\haohao') >>> p WindowsPath('C:/Users/haohao') ## 用 r'' (原始字串)就不用跳脫 >>> p = Path(r'C:\Users\haohao') >>> p WindowsPath('C:/Users/haohao') ## 建立 Path 可用前斜線表示 Windows 檔案路徑 >>> p = Path('C:/Users/haohao') >>> p WindowsPath('C:/Users/haohao') ## Mac 或 Linux 執行結果 -> 建立 PosixPath >>> p PosixPath('.') >>> p.resolve() PosixPath('/Users/haohao/Desktop')
透過前斜線(/)來連接子路徑的特殊語法
Path 物件可以用前斜線(/)來延伸檔案子目錄路徑,Path 可以指向資料夾、也可以指向檔案。
請注意:不管檔案或資料夾是否存在,Path 都可以指向它。
>>> p = Path('./Desktop') >>> p / 'test.txt' # 使用前斜線(/)與字串,就能指向檔案子目錄路徑 PosixPath('Desktop/test.txt') >>> p / 'test_dir' / 'test.txt' PosixPath('Desktop/test_dir/test.txt') >>> (p / 'test_dir' / 'test.txt').resolve() PosixPath('/Users/haohao/Desktop/test_dir/test.txt') ## 不管檔案或資料夾是否存在,都可以創造該路徑的 Path 物件 ## 操作檔案(例如刪除、讀寫)之前,用 exists() 檢查該路徑目標是否存在 >>> p.resolve() PosixPath('/Users/haohao/Desktop') >>> (p / 'foo.txt').exists() False >>> (p / 'test.txt').exists() # 桌面上確實存在 test.txt 檔案 True
檔案路徑字串操作
使用 Path 的函式可以很快地從檔案路徑字串中取出你需要的資訊。除了學習以下範例,pbpython 的 Cheat Sheet 也有很好記的圖片說明。
>>> p = (p / 'test.txt').resolve() >>> p PosixPath('/Users/haohao/Desktop/test.txt') >>> p.parent PosixPath('/Users/haohao/Desktop') >>> p.stem 'test' >>> p.suffix '.txt' >>> p.name 'test.txt'
簡易檔案讀寫
Path 除了對檔案路徑進行方便的字串處理,還可以進行檔案操作,包括創造、刪除檔案等等。如果你熟悉 Linux 的 Shell 操作,會發現 Path 使用的函式名稱跟 Linux Shell 指令很像!
# 範例:創造一個檔案,寫一句話,再把它刪除掉 >>> p = Path('/Users/haohao/Desktop') >>> file_name = 'test.txt' >>> p = p / file_name >>> p PosixPath('/Users/haohao/Desktop/test.txt') >>> p.exists() # 先確認這個檔案確實不存在 False ## 創造指向此路徑的檔案 >>> p.touch() # 創造 test.txt ## 寫入資料(return 寫入字串的長度) >>> p.write_text('hello world!') 12 >>> p.exists() # 現在此檔案存在了 True ## 讀取資料 >>> p.read_text() 'hello world!' ## 刪除檔案 >>> p.unlink() >>> p.exists() # 此檔案確實被刪掉了 False
Path 的讀寫除了用 write_text() 與 read_text(),你也可以使用 open()。
>>> with p.open() as f: f.readline() ... 'hello world!'
小提示
PurePath 與 Path 差別
如果你翻閱 Python pathlib 官方文件,會看到 Path 跟 PurePath 兩種類別、寫了不同的可用函式,實際上,Path 是 PurePath 的子類別(如圖示),PurePath 能用的函式、Path 幾乎都能用,所以大多時候用 Path 就好、你在 官方文件 看到的函式 Path 都能用。

PurePath 與 Path 在功能上的差別:PurePath 主要只是提供方便的檔案路徑字串處理,而 Path 是 PurePath 加上 System Call 操作,也就是說,需要用 Path 才能與作業系統互動,包括檢查檔案是否存在、讀寫檔案、查看使用者家目錄等功能,總之,大多時候就用 Path 吧!
# PurePath 與 Path 差別範例 ## 當你正在使用 Mac 或 Linux ## 你還是可以直接創造一個 Windows 的 PurePath 物件 >>> pathlib.PureWindowsPath() PureWindowsPath('.') ## 但是不能創造 WindowsPath 物件 ## 因為作業系統不同、無法呼叫 Windows 的 System Call >>> pathlib.WindowsPath() NotImplementedError: cannot instantiate 'WindowsPath' on your system
Path 的類別方法
有些檔案路徑操作不需要你先創造 Path 物件,用類別方法(class method)就能直接呼叫。
如果你對類別方法不是很理解,好豪在 這則筆記的 4-6 小節 記下了值得一看的教學文章。
## 目前所在路徑(Current Working Directory) >>> Path.cwd() PosixPath('/Users/haohao/Desktop') ## 使用者的家目錄路徑 >>> Path.home() PosixPath('/Users/haohao') ## 因為是 classmethod,所以從已創造的 Path 物件呼叫也可以 >>> p = Path() >>> p.cwd() PosixPath('/Users/haohao/Desktop') >>> p.home() PosixPath('/Users/haohao')
好豪自己覺得容易忘記的函式關鍵字
刪除:unlink()
檔案的移除常見函式關鍵字是 remove,例如 os.remove()。在 Path 的刪除則是使用 unlink()、與 Linux 指令相似,請參考上方 檔案讀寫範例。
所在資料夾:.parent
要查詢檔案或路徑所在的資料夾,直覺想到關鍵字會用 dir,例如 os.path.dirname()。Path 的關鍵字是用 parent,並且不是用函式、而是使用屬性(property),請看上方 用到 .parent 的範例。
如果你原本常用 os.path ,想跳來用 pathlib.Path
在 pathlib 官方文件的頁面尾端,貼心地提供了 os.path 與 pathlib.Path 的函式功能對應表格,推薦你閱讀、可以快速上手 Path 操作。
fastai 的 Path 使用
fastai 是建立在 Pytorch 之上的高階 API,就像大家愛用 Keras 簡化 Tensorflow 操作一樣,fastai 致力於讓基於 Pytorch 的深度學習流程更簡單。而 fastai 就用了 Path 來讓檔案操作更有效率,以下會列舉兩個例子。
ls
在 Linux 指令中超常用的指令,ls 會列出當下路徑內所有檔案與資料夾,Path 可以用 iterdir() 達成相似的功能。
fastai 用 Monkey Patch 的方式,直接在 Path 類別加上 ls 函式,筆者好豪在下方以簡單版的實現方式介紹:
## ls 函式在 Path 類別的 Monkey Patch >>> Path.ls = lambda x: [o.name for o in x.iterdir()] >>> p = Path() >>> p.ls() ['test_subdir', 'test_2.txt', 'test_3.txt', 'test_1.txt'] ## 使用 is_file() 或 is_dir(),只列出檔案、或只列出資料夾 >>> [o.name for o in p.iterdir() if o.is_dir()] ['test_subdir']
untar_data
fastai 內 untar_data() 的主要功能是下載並讀取資料,它會將資料存放成 Path 物件、而不是直接讀取檔案。以機器學習會用到的圖片資料集為例,常見存放方式是同個資料夾存放同一個標記(Label)的圖片、或者檔案名稱就是圖片內容標記等等。因此,untar_data() 如此設計,就可以用 Path 方便的路徑字串處理,快速取出標記。以下是 fastai 官方範例:
>>> from fastai.vision import untar_data >>> path = untar_data(URLs.PETS); path PosixPath('/tmp/.fastai/data/oxford-iiit-pet') >>> path.ls() [PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images'), PosixPath('/tmp/.fastai/data/oxford-iiit-pet/annotations')] >>> path_img = path/'images' >>> fnames = get_image_files(path_img) >>> fnames[:5] [PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images/japanese_chin_17.jpg'), PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images/japanese_chin_175.jpg'), PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images/shiba_inu_156.jpg'), PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images/english_cocker_spaniel_142.jpg'), PosixPath('/tmp/.fastai/data/oxford-iiit-pet/images/Siamese_47.jpg')] >>> fnames[0].stem # 從檔名獲得標記 'japanese_chin_17'
結語
pathlib.Path 用物件導向的方式,提供了方便的檔案路徑字串處理、以及簡易的檔案操作,並且 Path 的路徑處理在不同作業系統都能運作。當你的 Python 程式需要任何檔案路徑相關的處理,就快拿 Path 來用用看吧。
還想知道更多 Python 相關技巧嗎?推薦你閱讀我寫過的更多 Python 教學文章,學會更多 Pythonic Code:
如果這篇文章有幫助到你,歡迎追蹤 好豪的粉絲專頁,我會持續分享 Python 技巧、資料科學等知識;也歡迎點選下方按鈕將本文加入書籤、或者分享給更多正在學 Python 的朋友。