中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

python3的pathlib模塊:馴服文件系統(tǒng)

發(fā)布于:2021-01-21 09:24:36

0

193

0

python pathlib 文件系統(tǒng)

您在Python中處理文件路徑時(shí)遇到困難嗎?在Python 3.4及更高版本中,斗爭(zhēng)已經(jīng)結(jié)束!您不再需要像下面這樣寫代碼:

>>> path.rsplit('\', maxsplit=1)[0]

或畏縮于:

>>> os.path.isfile(os.path.join(os.path.expanduser('~'), 'realpython.txt'))

在本教程中,您將了解如何在Python中使用文件路徑(目錄和文件的名稱)。您將學(xué)習(xí)讀取和寫入文件,操縱路徑和基礎(chǔ)文件系統(tǒng)的新方法,并查看有關(guān)如何列出文件和對(duì)其進(jìn)行迭代的一些示例。使用該pathlib模塊,可以使用簡(jiǎn)潔,易讀和Pythonic的代碼,例如:

>>> path.parent >>> (pathlib.Path.home() / 'realpython.txt').is_file()

Python文件路徑處理問題

由于許多不同的原因,使用文件和與文件系統(tǒng)進(jìn)行交互非常重要。最簡(jiǎn)單的情況可能僅涉及讀取或?qū)懭胛募?,但是有時(shí)手頭上會(huì)遇到更復(fù)雜的任務(wù)。也許您需要列出給定類型的目錄中的所有文件,找到給定文件的父目錄,或者創(chuàng)建一個(gè)不存在的唯一文件名。傳統(tǒng)上,Python使用常規(guī)文本字符串表示文件路徑。在os.path標(biāo)準(zhǔn)庫(kù)的支持下,雖然有點(diǎn)麻煩(如引言中的第二個(gè)示例所示),但這已經(jīng)足夠了。然而,由于路徑不是字符串,重要的功能是分散各地的標(biāo)準(zhǔn)庫(kù),包括像圖書館os,glob和shutil。以下示例僅需要三個(gè)import語(yǔ)句即可將所有文本文件移動(dòng)到存檔目錄:

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

對(duì)于由字符串表示的路徑,使用常規(guī)字符串方法是可能的,但通常是個(gè)壞主意。例如,不要像常規(guī)字符串那樣用+連接兩個(gè)路徑,而應(yīng)該使用os.path.join(),它使用操作系統(tǒng)上正確的路徑分隔符連接路徑。回想一下,Windows使用而Mac和Linux使用/作為分隔符。這種差異可能導(dǎo)致難以發(fā)現(xiàn)的錯(cuò)誤,例如我們?cè)诤?jiǎn)介中的第一個(gè)示例僅適用于Windows路徑。

Python 3.4(PEP 428)中引入了pathlib模塊來(lái)應(yīng)對(duì)這些挑戰(zhàn)。它在一個(gè)地方收集必要的功能,并通過一個(gè)易于使用的Path對(duì)象上的方法和屬性使其可用。

早期,其他軟件包仍然使用字符串作為文件路徑,但從Python 3.6開始,整個(gè)標(biāo)準(zhǔn)庫(kù)都支持pathlib模塊,部分原因是添加了文件系統(tǒng)路徑協(xié)議。如果您還停留在傳統(tǒng)的Python上,那么Python2也有一個(gè)backport可用。

行動(dòng)時(shí)間:讓我們看看pathlib在實(shí)踐中是如何工作的。

創(chuàng)建路徑

您真正需要了解的只是pathlib.Path課程。創(chuàng)建路徑有幾種不同的方法。首先,有一些類方法,例如 .cwd()(當(dāng)前工作目錄)和.home()(用戶的主目錄):

>>> import pathlib >>> pathlib.Path.cwd() PosixPath('/home/gahjelle/realpython/')

注意:在本教程中,我們假設(shè)pathlib已導(dǎo)入,而沒有將import pathlib拼寫為上面。由于您將主要使用Path類,因此還可以執(zhí)行from pathlib import Path并編寫Path而不是pathlib.Path。

也可以從其字符串表示形式顯式創(chuàng)建路徑:

>>> pathlib.Path(r'C:Usersgahjellerealpythonfile.txt') WindowsPath('C:/Users/gahjelle/realpython/file.txt')

處理Windows路徑的小提示:在Windows上,路徑分隔符是反斜杠,。但是,在許多上下文中,反斜杠也用作轉(zhuǎn)義字符,以表示不可打印的字符。為了避免出現(xiàn)問題,請(qǐng)使用原始字符串文本來(lái)表示W(wǎng)indows路徑。這些字符串文字前面有一個(gè)r。在原始字符串文本中,表示文本反斜杠:r'C:Users'。

構(gòu)造路徑的第三種方法是使用特殊運(yùn)算符連接路徑的各個(gè)部分。正斜杠運(yùn)算符獨(dú)立于平臺(tái)上的實(shí)際路徑分隔符使用:

>>> pathlib.Path.home() / 'python' / 'scripts' / 'test.py' PosixPath('/home/gahjelle/python/scripts/test.py')

只要存在至少一個(gè)Path對(duì)象,可以連接多個(gè)路徑或路徑和字符串的混合(如上所述)。如果您不喜歡特殊/符號(hào),則可以使用以下.joinpath()方法執(zhí)行相同的操作:

>>> pathlib.Path.home().joinpath('python', 'scripts', 'test.py') PosixPath('/home/gahjelle/python/scripts/test.py')

請(qǐng)注意,在前面的示例中,pathlib.Path表示為aWindowsPath或a PosixPath。表示路徑的實(shí)際對(duì)象取決于基礎(chǔ)操作系統(tǒng)。(也就是說,WindowsPath示例在Windows上運(yùn)行,而PosixPath示例在Mac或Linux上運(yùn)行。)有關(guān)更多信息,請(qǐng)參見操作系統(tǒng)差異部分。

讀寫文件

傳統(tǒng)上,用Python讀取或?qū)懭胛募姆椒ㄒ恢笔鞘褂脙?nèi)置open()函數(shù)。這仍然是正確的,因?yàn)閛pen()函數(shù)可以Path直接使用對(duì)象。以下示例在Markdown文件中查找所有標(biāo)頭并進(jìn)行打?。?/span>

path = pathlib.Path.cwd() / 'test.md' with open(path, mode='r') as fid:     headers = [line.strip() for line in fid if line.startswith('#')] print('n'.join(headers))

一個(gè)等效的替代方法是在Path對(duì)象上調(diào)用.open():

with path.open(mode='r') as fid:     ...

其實(shí)Path.open()是所謂的內(nèi)置open()后臺(tái)。您使用哪種選擇主要取決于口味。

為了簡(jiǎn)單地讀取和寫入文件,pathlib庫(kù)中提供了兩種便捷方法:

  • .read_text():在文本模式下打開路徑,然后將內(nèi)容作為字符串返回。

  • .read_bytes():以二進(jìn)制/字節(jié)模式打開路徑,并將內(nèi)容作為字節(jié)串返回。

  • .write_text():打開路徑并向其中寫入字符串?dāng)?shù)據(jù)。

  • .write_bytes():以二進(jìn)制/字節(jié)模式打開路徑并將數(shù)據(jù)寫入其中。

這些方法中的每一個(gè)都處理文件的打開和關(guān)閉,使它們使用起來(lái)很簡(jiǎn)單,例如:

>>> path = pathlib.Path.cwd() / 'test.md' >>> path.read_text() <the contents of the test.md-file>

路徑也可以指定為簡(jiǎn)單文件名,在這種情況下,它們相對(duì)于當(dāng)前工作目錄進(jìn)行解釋。以下示例與上一個(gè)示例等效:

>>> pathlib.Path('test.md').read_text() <the contents of the test.md-file>

該.resolve()方法將找到完整路徑。在下面,我們確認(rèn)當(dāng)前工作目錄已用于簡(jiǎn)單文件名:

>>> path = pathlib.Path('test.md') >>> path.resolve() PosixPath('/home/gahjelle/realpython/test.md') >>> path.resolve().parent == pathlib.Path.cwd() True >>> path.parent == pathlib.Path.cwd() False

請(qǐng)注意,比較路徑時(shí),將比較它們的表示。在上面的示例中,path.parent不等于pathlib.Path.cwd(),因?yàn)閜ath.parent由表示,'.'而pathlib.Path.cwd()由表示'/home/gahjelle/realpython/'。

挑選路徑的組成部分

路徑的不同部分可以方便地用作屬性?;臼纠ǎ?/span>

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

  • .parent:包含文件的目錄;如果path是目錄,則為父目錄

  • .stem:不帶后綴的文件名

  • .suffix:文件擴(kuò)展名

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

這些屬性正在起作用:

>>> path PosixPath('/home/gahjelle/realpython/test.md') >>> path.name 'test.md' >>> path.stem 'test' >>> path.suffix '.md' >>> path.parent PosixPath('/home/gahjelle/realpython') >>> path.parent.parent PosixPath('/home/gahjelle') >>> path.anchor '/'

請(qǐng)注意,它.parent返回一個(gè)新Path對(duì)象,而其他屬性則返回字符串。例如,這意味著.parent可以像上一個(gè)示例中那樣進(jìn)行鏈接,甚至可以結(jié)合使用/以創(chuàng)建全新的路徑:

>>> path.parent.parent / ('new' + path.suffix) PosixPath('/home/gahjelle/new.md')

出色的Pathlib備忘單提供了這些以及其他屬性和方法的直觀表示。

移動(dòng)和刪除文件

通過pathlib,您還可以訪問基本文件系統(tǒng)級(jí)別的操作,如移動(dòng),更新,甚至刪除文件。在大多數(shù)情況下,這些方法在信息或文件丟失之前不會(huì)發(fā)出警告或等待確認(rèn)。使用這些方法時(shí)要小心。

要移動(dòng)文件,請(qǐng)使用.replace()。請(qǐng)注意,如果目標(biāo)已經(jīng)存在,.replace()則將其覆蓋。不幸的是,pathlib它不明確支持文件的安全移動(dòng)。為了避免可能覆蓋目標(biāo)路徑,最簡(jiǎn)單的方法是在替換之前測(cè)試目標(biāo)是否存在:

if not destination.exists():     source.replace(destination)

但是,這確實(shí)會(huì)使車門保持打開狀態(tài),以防可能發(fā)生比賽。另一個(gè)進(jìn)程可能會(huì)destination在if語(yǔ)句執(zhí)行和.replace()方法之間的路徑處添加文件。如果這是一個(gè)問題,一種更安全的方法是打開用于獨(dú)占創(chuàng)建的目標(biāo)路徑并顯式復(fù)制源數(shù)據(jù):

with destination.open(mode='xb') as fid:     fid.write(source.read_bytes())

上面的代碼將引發(fā)一個(gè)(FileExistsError如果destination已經(jīng)存在)。從技術(shù)上講,這將復(fù)制文件。要執(zhí)行移動(dòng),只需source在復(fù)制完成后刪除即可(請(qǐng)參見下文)。確保沒有異常。

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

例如:

>>> path PosixPath('/home/gahjelle/realpython/test001.txt') >>> path.with_suffix('.py') PosixPath('/home/gahjelle/realpython/test001.py') >>> path.replace(path.with_suffix('.py'))

目錄和文件可以分別使用.rmdir()和刪除.unlink()。(再次,小心?。?/span>

例子

在本節(jié)中,您將看到一些有關(guān)如何使用它們pathlib來(lái)應(yīng)對(duì)簡(jiǎn)單挑戰(zhàn)的示例。

計(jì)數(shù)文件

有幾種列出許多文件的方法。最簡(jiǎn)單的.iterdir()方法是迭代給定目錄中的所有文件。以下示例.iterdir()與collections.Counter該類組合以計(jì)算當(dāng)前目錄中每種文件類型的文件數(shù)量:

>>> import collections >>> collections.Counter(p.suffix for p in pathlib.Path.cwd().iterdir()) Counter({'.md': 2, '.txt': 4, '.pdf': 2, '.py': 1})

更靈活的文件列表可以用這些方法來(lái)創(chuàng)建.glob()和.rglob()(遞歸水珠)。例如,pathlib.Path.cwd().glob('*.txt')返回.txt當(dāng)前目錄中所有帶后綴的文件。以下僅計(jì)算以開頭的文件類型p:

>>> import collections >>> collections.Counter(p.suffix for p in pathlib.Path.cwd().glob('*.p*')) Counter({'.pdf': 2, '.py': 1})

顯示目錄樹

下一個(gè)示例定義一個(gè)函數(shù),該函數(shù)tree()將打印一個(gè)可視樹,該可視樹表示以給定目錄為根的文件層次結(jié)構(gòu)。在這里,我們也要列出子目錄,因此我們使用.rglob()方法:

def tree(directory):     print(f'+ {directory}')     for path in sorted(directory.rglob('*')):         depth = len(path.relative_to(directory).parts)         spacer = '    ' * depth         print(f'{spacer}+ {path.name}')

請(qǐng)注意,我們需要知道文件離根目錄有多遠(yuǎn)。為此,我們首先使用.relative_to()代表相對(duì)于根目錄的路徑。然后,我們計(jì)算表示形式中目錄的數(shù)量(使用.parts屬性)。運(yùn)行時(shí),此函數(shù)將創(chuàng)建如下的可視樹:

>>> tree(pathlib.Path.cwd()) + /home/gahjelle/realpython     + directory_1         + file_a.md      + directory_2         + file_a.md          + file_b.pdf             + file_c.py      + file_1.txt      + file_2.txt

注:在F-串僅在Python 3.6及更高版本。在較舊的Python中,f'{spacer}+ {path.name}'可以編寫表達(dá)式'{0}+ {1}'.format(spacer, path.name)。

查找最后修改的文件

.iterdir(),.glob()和.rglob()方法都是偉大的擬合生成器表達(dá)式和列表內(nèi)涵。要在最后修改的目錄中查找文件,可以使用該.stat()方法獲取有關(guān)基礎(chǔ)文件的信息。例如,.stat().st_mtime給出文件的最后修改時(shí)間:

>>> from datetime import datetime >>> time, file_path = max((f.stat().st_mtime, f) for f in directory.iterdir()) >>> print(datetime.fromtimestamp(time), file_path) 2018-03-23 19:23:56.977817 /home/gahjelle/realpython/test001.txt

您甚至可以獲取使用類似表達(dá)式最后修改的文件的內(nèi)容:

>>> max((f.stat().st_mtime, f) for f in directory.iterdir())[1].read_text() <the contents of the last modified file in directory>

從不同.stat().st_屬性返回的時(shí)間戳表示自1970年1月1日以來(lái)的秒數(shù)。除或之外datetime.fromtimestamp,還可以用于將時(shí)間戳轉(zhuǎn)換為更可用的時(shí)間戳。time.localtimetime.ctime。

創(chuàng)建一個(gè)唯一的文件名

最后一個(gè)示例將展示如何基于模板構(gòu)造唯一的編號(hào)文件名。首先,為文件名指定一個(gè)模式,并為計(jì)數(shù)器留出空間。然后,檢查是否存在通過連接目錄和文件名(帶有計(jì)數(shù)器的值)創(chuàng)建的文件路徑。如果已經(jīng)存在,請(qǐng)?jiān)黾佑?jì)數(shù)器,然后重試:

def unique_path(directory, name_pattern):     counter = 0     while True:         counter += 1         path = directory / name_pattern.format(counter)         if not path.exists():             return pathpath = unique_path(pathlib.Path.cwd(), 'test{:03d}.txt')

如果目錄已經(jīng)包含test001.txt和test002.txt,則上面的代碼將設(shè)置path為test003.txt。

操作系統(tǒng)差異

之前,我們注意到實(shí)例化時(shí)pathlib.Path,返回了WindowsPath或PosixPath對(duì)象。對(duì)象的類型取決于您使用的操作系統(tǒng)。此功能使編寫跨平臺(tái)兼容的代碼變得相當(dāng)容易??梢砸笠粋€(gè)WindowsPath或一個(gè)PosixPath顯式的,但是您只會(huì)將代碼限制在該系統(tǒng)上而沒有任何好處。這樣的具體路徑不能在其他系統(tǒng)上使用:

>>> pathlib.WindowsPath('test.md') NotImplementedError: cannot instantiate 'WindowsPath' on your system

有時(shí),您可能需要表示路徑而不訪問底層文件系統(tǒng)(在這種情況下,在非Windows系統(tǒng)上表示W(wǎng)indows路徑也可能是有意義的,反之亦然)。這可以通過PurePath對(duì)象來(lái)完成。這些對(duì)象支持“路徑組件”部分中討論的操作,但不支持訪問文件系統(tǒng)的方法:

>>> path = pathlib.PureWindowsPath(r'C:Usersgahjellerealpythonfile.txt') >>> path.name 'file.txt' >>> path.parent PureWindowsPath('C:/Users/gahjelle/realpython') >>> path.exists() AttributeError: 'PureWindowsPath' object has no attribute 'exists'

您可以直接實(shí)例化PureWindowsPath或PurePosixPath在所有系統(tǒng)上。實(shí)例化PurePath將根據(jù)您使用的操作系統(tǒng)返回這些對(duì)象之一。

路徑作為適當(dāng)?shù)膶?duì)象

在簡(jiǎn)介中,我們簡(jiǎn)要地指出,路徑不是字符串,其背后的動(dòng)機(jī)之一pathlib是用適當(dāng)?shù)膶?duì)象表示文件系統(tǒng)。實(shí)際上,的正式文檔pathlib標(biāo)題為pathlib—面向?qū)ο蟮奈募到y(tǒng)路徑。在面向?qū)ο蟮姆椒ㄒ呀?jīng)在上面的例子中(特別是如果你用舊它對(duì)比相當(dāng)明顯os.path的處事方式)。但是,讓我留下一些其他花絮。

與所使用的操作系統(tǒng)無(wú)關(guān),路徑以Posix樣式表示,并使用正斜杠作為路徑分隔符。在Windows上,您將看到以下內(nèi)容:

>>> pathlib.Path(r'C:Usersgahjellerealpythonfile.txt') WindowsPath('C:/Users/gahjelle/realpython/file.txt')

盡管如此,當(dāng)路徑轉(zhuǎn)換為字符串時(shí),它將使用本機(jī)形式,例如在Windows上帶有反斜杠:

>>> str(pathlib.Path(r'C:Usersgahjellerealpythonfile.txt')) 'C:\Users\gahjelle\realpython\file.txt'

如果您使用的庫(kù)不知道如何處理pathlib.Path對(duì)象,這將特別有用。在3.6之前的Python版本上,這是一個(gè)更大的問題。例如,在Python 3.5,該configparser標(biāo)準(zhǔn)庫(kù)只能使用字符串路徑讀取文件。處理此類情況的方法是顯式轉(zhuǎn)換為字符串:

>>> from configparser import ConfigParser >>> path = pathlib.Path('config.txt') >>> cfg = ConfigParser() >>> cfg.read(path)                    # Error on Python < 3.6 TypeError: 'PosixPath' object is not iterable >>> cfg.read(str(path))          # Works on Python >= 3.4 ['config.txt']

在Python 3.6及更高版本中os.fspath(),str()如果需要進(jìn)行顯式轉(zhuǎn)換,建議使用代替。這樣比較安全,因?yàn)槿绻恍⌒膰L試轉(zhuǎn)換非路徑類的對(duì)象,則會(huì)引發(fā)錯(cuò)誤。

pathlib庫(kù)中最不常見的部分可能是/運(yùn)算符的使用。稍微了解一下,讓我們看看它是如何實(shí)現(xiàn)的。這是運(yùn)算符重載的一個(gè)示例:運(yùn)算符的行為根據(jù)上下文而改變。您之前已經(jīng)看過??紤]一下+對(duì)于字符串和數(shù)字來(lái)說,不同的含義是什么意思。Python通過使用雙下劃線方法(又稱dunder方法)實(shí)現(xiàn)運(yùn)算符重載。

在操作者通過所定義的.__truediv__()方法。實(shí)際上,如果您查看的源代碼pathlib,則會(huì)看到類似以下內(nèi)容的內(nèi)容:

class PurePath(object):     def __truediv__(self, key):         return self._make_child((key,))

結(jié)論

從Python 3.4開始,pathlib標(biāo)準(zhǔn)庫(kù)中已提供該功能。使用pathlib,文件路徑可以由適當(dāng)?shù)腜ath對(duì)象表示,而不是像以前那樣由純字符串表示。這些對(duì)象使代碼處理文件路徑:

  • 易于閱讀,尤其是因?yàn)樗?是用于將路徑連接在一起的

  • 功能更強(qiáng)大,具有直接在對(duì)象上可用的大多數(shù)必要方法和屬性

  • 跨操作系統(tǒng)更一致,因?yàn)镻ath對(duì)象隱藏了不同系統(tǒng)的特性

在本教程中,您已經(jīng)了解了如何創(chuàng)建Path對(duì)象,讀取和寫入文件,操作路徑和基礎(chǔ)文件系統(tǒng),以及一些如何遍歷許多文件路徑的示例。