幾年前,當(dāng)我發(fā)現(xiàn) Python 的新 pathlib 模塊時(shí),我最初認(rèn)為它是 os.path 模塊的一個(gè)稍微笨拙和不必要的面向?qū)ο蟀姹?。我錯(cuò)了。Python 的 pathlib 模塊實(shí)際上很棒!
在本文中,我將嘗試在pathlib上向你推銷。我希望本文將激勵(lì)你在任何需要使用 Python 中的文件時(shí)使用 Python 的 pathlib 模塊。
os.path 笨拙
os.path 模塊一直是我們用來處理 Python 中的路徑的庫。你需要的東西差不多都包含在內(nèi)了,但有時(shí)它會(huì)很顯得笨重。
你應(yīng)該像這樣導(dǎo)入它?
import os.path
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
還是像這樣?
from os.path import abspath, dirname, join
BASE_DIR = dirname(dirname(abspath(__file__)))
TEMPLATES_DIR = join(BASE_DIR, 'templates')
或者,該 join 函數(shù)的命名過于籠統(tǒng),我們還可以這樣做:
from os.path import abspath, dirname, join as joinpath
BASE_DIR = dirname(dirname(abspath(__file__)))
TEMPLATES_DIR = joinpath(BASE_DIR, 'templates')
但是,我覺得這些都有點(diǎn)尷尬。我們將字符串傳遞到返回字符串的函數(shù)中,然后又將其傳遞給返回字符串的其他函數(shù)。這些字符串剛好可以表示路徑,但它們?nèi)匀恢皇亲址?/p>
當(dāng)多個(gè)函數(shù)嵌套時(shí),os.path 中字符串進(jìn)字符串出類的函數(shù)非常笨拙,我們需要從內(nèi)向外來閱讀代碼。如果我們可以把這些嵌套的函數(shù)調(diào)用轉(zhuǎn)換成鏈?zhǔn)椒椒ㄕ{(diào)用,這不是很好嗎?
有了 pathlib 模塊,我們就可以了!
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES_DIR = BASE_DIR.joinpath('templates')
os.path 模塊需要函數(shù)嵌套,但是 pathlib 模塊的 path 類允許我們鏈?zhǔn)讲僮?Path 對象上的方法和屬性,以獲得等效的路徑表示。
也許你在想:等等,這些路徑對象不是一回事:它們是對象,不是路徑字符串!不急,我們將稍后來討論這個(gè)問題(提示:這些字符串幾乎可以與路徑字符串互換使用)。
os 模塊臃腫
Python 經(jīng)典模塊 os.path 只用于處理路徑。一旦你真的想通過路徑做一些事情(例如創(chuàng)建一個(gè)目錄),你就需要用到另一個(gè) Python 模塊,通常也是 os 下的模塊。
os 模塊有許多用于處理文件和目錄的工具,比如:mkdir、getcwd、chmod、stat、remove、rename 和 rmdir。還有 chdir、link、wald、listdir、makedirs、rename、remvedirs、unlink (與remove相同) 以及 symlink。此外,還有一大堆與文件系統(tǒng)完全無關(guān)的東西:fork、getenv、putenv、environ、getlogin 和 system。還有很多在這里沒有羅列的東西。
Python 的 os 模塊什么都能做一點(diǎn);它有點(diǎn)像是系統(tǒng)相關(guān)東西的大雜燴。盡管 os 模塊中有很多不錯(cuò)的東西,但有時(shí)也可能很難找到你要的東西:比如你想在 os模塊中查找與路徑或文件系統(tǒng)相關(guān)的內(nèi)容,則需要進(jìn)一步挖掘。
pathlib 模塊用 path 對象上的方法替換了許多這些與文件系統(tǒng)相關(guān)的 os 功能。
以下代碼創(chuàng)建了src/_pypackages_目錄,并將 .editorconfig 文件重命名為 src/.editorconfig:
import os
import os.path
os.makedirs(os.path.join('src', '__pypackages__'), exist_ok=True)
os.rename('.editorconfig', os.path.join('src', '.editorconfig'))
使用 Path 對象執(zhí)行相同的操作:
from pathlib import Path
Path('src/__pypackages__').mkdir(parents=True, exist_ok=True)
Path('.editorconfig').rename('src/.editorconfig')
注意,由于鏈?zhǔn)椒椒?,pathlib 代碼將路徑放在第一位!
正如 Python 之禪所說,“名稱空間是一個(gè)很棒的想法,讓我們做更多的事情”。os 模塊是一個(gè)非常大的名稱空間,里面有一堆東西。pathlib.path 類是一個(gè)比 os 模塊更小、目標(biāo)更明確的命名空間。此路徑命名空間中的方法返回路徑對象,允許方法鏈?zhǔn)讲僮鞫皇亲址唇邮降那短缀瘮?shù)調(diào)用。
別忘了還有 GLOB 模塊!
os 和 os.path 模塊并不是 Python 標(biāo)準(zhǔn)庫中唯一與文件路徑/文件系統(tǒng)相關(guān)的功能模塊。glob 模塊是另一個(gè)處理路徑相關(guān)的模塊。
我們可以使用 glob.glob 函數(shù)查找與特定模式匹配的文件:
from glob import glob
top_level_csv_files = glob('*.csv')
all_csv_files = glob('**/*.csv', recursive=True)
新的 pathlib 模塊同樣包括類似 glob 的功能。
from pathlib import Path
top_level_csv_files = Path.cwd().glob('*.csv')
all_csv_files = Path.cwd().rglob('*.csv')
當(dāng)重度使用 pathlib 之后,你可能會(huì)完全忘記 glob 模塊的存在: PATH 對象已經(jīng)提供了的所有 glob 模塊所具備的功能。
pathlib 讓簡單變得更簡單
pathlib 模塊將許多復(fù)雜的情況變得簡單,但它也可以使一些簡單的事情變得更簡單。
需要讀取一個(gè)或多個(gè)文件中的所有文本?
你可以使用 with 語句塊打開文件、讀取其內(nèi)容然后關(guān)閉文件:
from glob import glob
file_contents = []
for filename in glob('**/*.py', recursive=True):
with open(filename) as python_file:
file_contents.append(python_file.read())
或者,你可以用 Path 對象的 read_text 方法,在一行代碼中用列表解析功能將文件內(nèi)容讀取到一個(gè)新列表中:
from pathlib import Path
file_contents = [
path.read_text()
for path in Path.cwd().rglob('*.py')
]
如果我需要寫入文件呢?
你可以使用 open 上下文管理器:
with open('.editorconfig') as config:
config.write('# config goes here')
或者使用 write_text 方法:
Path('.editorconfig').write_text('# config goes here')
如果你更喜歡使用 open (無論是作為上下文管理器還是其他方式),你同樣可以在 PATH 對象上使用 OPEN 方法:
from pathlib import Path
path = Path('.editorconfig')
with path.open(mode='wt') as config:
config.write('# config goes here')
或者,從 Python3.6 開始,你甚至可以將 PATH 對象傳遞給內(nèi)置的 open 函數(shù):
from pathlib import Path
path = Path('.editorconfig')
with open(path, mode='wt') as config:
config.write('# config goes here')
PATH 對象讓你的代碼更加明確
以下三個(gè)變量指向什么?它們的值代表什么?
person = '{"name": "Trey Hunner", "location": "San Diego"}'
pycon_2019 = "2019-05-01"
home_directory = '/home/trey'
這些變量中的每一個(gè)都指向一個(gè)字符串。
這些字符串表示不同的東西:一個(gè)是 JSON blob,一個(gè)是日期,還有一個(gè)是文件路徑。
以下是這些對象更有用的表示:
from datetime import date
from pathlib import Path
person = {"name": "Trey Hunner", "location": "San Diego"}
pycon_2019 = date(2019, 5, 1)
home_directory = Path('/home/trey')
JSON 對象可以反序列化到字典,日期在本地使用 datetime.date 對象表示,文件系統(tǒng)路徑現(xiàn)在可以使用 pathlib.path 對象統(tǒng)一表示。
使用 Path 對象可以使代碼更加明確。如果要表示日期,則可以使用 Date 對象。如果試圖表示文件路徑,則可以使用 Path 對象。
我并非面向?qū)ο缶幊痰膱?jiān)定擁護(hù)者。類增加了另一層抽象層,而抽象有時(shí)會(huì)增加更多的復(fù)雜性無法保持簡單化。但是 pathlib.Path 類是一個(gè)有用的抽象。它也正在迅速成為一個(gè)普遍接受的抽象。
感謝 PEP519,文件路徑對象現(xiàn)在成為使用路徑的標(biāo)準(zhǔn)。在 Python3.6 中,內(nèi)置的 open 函數(shù)以及 os、shutil 和 os.path 模塊中的各種函數(shù)都可以與 pathlib.path 對象一起正常工作。你可以從現(xiàn)在開始使用 pathlib,而不需要改動(dòng)大多數(shù)使用路徑的代碼!
pathlib 還缺少什么?
雖然 pathlib 是偉大的,但它并不是包羅萬象的。我無意中發(fā)現(xiàn)了一些缺失的特性,我希望 pathlib 模塊能夠包括這些特性。
我注意到的第一個(gè)缺陷是 pathlib.Path 的方法中缺少與 shutil 等效的功能。
雖然可以將路徑對象(和類似路徑的對象)傳遞給高層的 shutil 函數(shù)進(jìn)行復(fù)制/刪除/移動(dòng)文件和目錄的操作,但是 Path 對象上沒有與此等效的方法。
因此,要復(fù)制文件,你仍然需要執(zhí)行如下操作:
from pathlib import Path
from shutil import copyfile
source = Path('old_file.txt')
destination = Path('new_file.txt')
copyfile(source, destination)
同樣也沒有與 os.chdir 等效的 pathlib 功能。
這意味著如果需要更改當(dāng)前工作目錄,仍需要導(dǎo)入 chdir:
from pathlib import Path
from os import chdir
parent = Path('..')
chdir(parent)
也沒有與 os.walk 函數(shù)等價(jià)的 pathlib 函數(shù)。盡管你可以很容易地使用 pathlib 創(chuàng)建你自己的 walk 式函數(shù)。
我希望 pathlib.Path 對象最終可以包含其中一些缺失操作的方法。盡管存在這些缺失的特性,我仍然覺得使用“ pathlib 系”比使用“ os.path 系”更易于管理。
你是否應(yīng)該始終使用 pathlib?
從Python3.6開始,pathlib.Path 對象幾乎可以在任何已經(jīng)使用路徑字符串的地方工作。因此,如果你使用的是Python3.6(或更高版本),我認(rèn)為沒有理由不使用 pathlib。
如果您使用的是 python 3 的早期版本, 則可以將 path 對象始終包裝在 str 調(diào)用中, 以便在需要轉(zhuǎn)義填充字符串的地方獲取相應(yīng)的字符串返回。雖然有點(diǎn)尷尬, 但還是管用:
from os import chdir
from pathlib import Path
chdir(Path('/home/trey')) # Python 3.6+ 版
chdir(str(Path('/home/trey'))) # 早期的 Python 3 版
不管你使用的是哪個(gè)版本的 Python3,我都建議你嘗試一下 pathlib。
什么?你還在用 Python2 ?好吧,萬能的 PyPI 上有個(gè)第三方工具 pathlib2 模塊是一個(gè)選擇,現(xiàn)在,你可以在任何版本的 Python 上使用 pathlib 啦!。
用 pathlib 讓我的代碼更具可讀性。現(xiàn)在,我處理文件的大多數(shù)代碼都默認(rèn)使用 pathlib,我建議你也這樣做。如果可以用 pathlib,則盡量用吧。
原文:https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/