pathlib
模塊是在Python3.4
版本中首次被引入到標準庫中的,作爲一個可選模塊。
從Python3.6
開始,內置的 open
函數以及 os
、 shutil
和 os.path
模塊中的各種函數都可以正確地使用 pathlib.Path
對象了。
最初,pathlib
給人的感覺只是os.path
的一個不必要的面向對象版本,
不過,當你實際去了解pathlib
之後,會發現pathlib
實際上絕不是一個簡單的面向對象版本,
而是實實在在的解決了os.path
存在的一些問題。
1. os.path VS pathlib
1.1. 路徑規範化
對於os.path
來說,路徑的分隔用正斜杆(\
)還是反斜槓(/
)需要自己根據操作系統來確定。
或者,每一個路徑拼接的地方,都用os.path.join
來連接。
而使用pathlib
的話,直接用反斜槓(/
)即可,不用擔心操作系統的不同。
比如:
import os
# windows系統中測試
os.path.join("a/b", "c.txt")
# 運行結果 錯誤
# 'a/b\\c.txt'
os.path.join("a", "b", "c.txt")
# 運行結果 正確
# 'a\\b\\c.txt'
從代碼可以看出,每一層文件夾都必須用join
連接才能正確適應不同系統。
而在pathlib
中,則不需要考慮這麼多。
from pathlib import Path
Path("a/b").joinpath("c.txt")
# WindowsPath('a/b/c.txt')
Path("a").joinpath("b").joinpath("c.txt")
# WindowsPath('a/b/c.txt')
使用pathlib
,在windows
或者linux
中,統一使用反斜槓(/
)來分隔文件夾。
路徑規範化之後的好處就是代碼更加簡潔。
比如:下面這個重命名文件的例子(a/b/c/d.csv => a/b/c.csv
)
# os.path 方式
os.rename(os.path.join("a", "b", "c", "d.csv"), os.path.join("a", "b", "c.csv"))
# pathlib 方式
Path("a/b/c/d.csv").reanme("a/b/c.csv")
哪種方式更清晰簡潔不言而喻。
1.2. 字符串和對象
爲什麼要用對象來表示路徑?
先看下面3個字符串變量:
student = '{"name": "databook", "score": "90"}'
graduate_date = "2023-07-01"
home_directory = '/home/databook'
這3個字符串其實代表不同的事物:一種是 JSON blob,一種是日期,一種是文件路徑。
再看下面3個用對象表示的變量:
from datetime import date
from pathlib import Path
student = {"name": "databook", "score": "90"}
graduate_date = date(2023, 7, 1)
home_directory = Path('/home/databook')
用字符串來表示變量確實簡潔,但也導致每個變量失去了其本身的意義,
程序無法區分這個變量代表的是JSON,還是日期,還是一個路徑,從而增加了程序的不確定性。
程序規模大了,或者複雜性提高了之後,存在很大的隱患。
os.path
和pathlib
就是這樣的關係,os.path
使用字符串表示路徑,pathlib
使用Path
對象表示路徑。
1.3. 讀寫文件
pathlib
的路徑對象(Path
)可以直接讀寫文件,因此也能大大簡化讀寫文件的代碼。
不用pathlib
的讀寫文件方式:
import os
# 讀取文件
fp = os.path.join("a", "b.txt")
with open(fp, "r") as f:
f.read()
# 寫入文件
with open(fp, "w") as f:
f.write("hello")
使用pathlib
的話:
from pathlib import Path
# 讀取文件
Path("a/b.txt").read_text()
# 寫入文件
Path("a/b.txt").write_text("hello)
2. pathlib的性能
pathlib
用面向對象的方式處理路徑,難免讓人覺得會比傳統的方式慢很多,也就是存在性能問題。
那麼,pathlib
到底會比傳統方式慢多少?通過下面的簡單示例來看看。
傳統方式:
def a(d="D:/miniconda3/Lib/site-packages"):
from os import getcwd, walk
extension = ".py"
count = 0
for root, directories, filenames in walk(d):
for filename in filenames:
if filename.endswith(extension):
count += 1
print(f"{count} Python files found")
if __name__ == "__main__":
import time
t0 = time.time()
a()
t1 = time.time()
print(t1 - t0)
# 運行結果:
7875 Python files found
0.31201744079589844
pathlib
方式:
def b(d="D:/miniconda3/Lib/site-packages"):
from pathlib import Path
extension = ".py"
count = 0
for filename in Path(d).rglob(f"*{extension}"):
count += 1
print(f"{count} Python files found")
if __name__ == "__main__":
import time
t0 = time.time()
b()
t1 = time.time()
print(t1 - t0)
# 運行結果:
7875 Python files found
0.44898128509521484
讀取的標準庫中的文件,總共將近8000
個文件,運行多次後,時間大概相差0.1秒左右。pathlib
的性能確實略遜於傳統方式,但是將近8000
個文件,也只慢了0.1秒,
如果不是大規模處理文件的話,還是用pathlib
更好。
3. 總結
總的來說,與傳統的 os.path
模塊相比,pathlib
提供了一種更現代和麪向對象的方式來處理文件路徑。
它支持跨平臺的文件路徑操作,使得開發者可以更容易地編寫可移植的代碼。
此外,pathlib
還提供了鏈式調用的能力,使得代碼更加簡潔和易讀。
因此,爲了代碼更加簡潔、易讀和可維護,推薦使用 pathlib
來替代傳統的 os.path
。