求求你,別用 Os.Path 了
前段時(shí)間,在使用新版本的 Django 時(shí),我發(fā)現(xiàn)了 settings.py 的第一行代碼從
- import os
 - BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
變成了
- from pathlib import Path
 - BASE_DIR = Path(__file__).resolve().parent.parent
 
于是我就好奇,os 和 pathlib 同樣是標(biāo)準(zhǔn)庫(kù),為什么 pathlib 得到了 Django 的青睞?學(xué)習(xí)了一番 pathlib 之后,發(fā)現(xiàn)這是一個(gè)非常高效便捷的工具,用它來(lái)處理文件系統(tǒng)路徑相關(guān)的操作最合適不過(guò),集成了很多快捷的功能,提升你的編程效率,那是妥妥的。
接下來(lái)讓一起看一下,為什么 pathlib 更值得我們使用。
pathlib vs os
話(huà)不多說(shuō),先看下使用對(duì)比:比如說(shuō)
打印當(dāng)前的路徑:
使用 os:
- In [13]: import os
 - In [14]: os.getcwd()
 - Out[14]: '/Users/aaron'
 
使用 pathlib:
- In [15]: from pathlib import Path
 - In [16]: Path.cwd()
 - Out[16]: PosixPath('/Users/aaron')
 - In [17]: print(Path.cwd())
 - /Users/aaron
 
使用 print 打印的結(jié)果是一樣的,但 os.getcwd() 返回的是字符串,而 Path.cwd() 返回的是 PosixPath 類(lèi),你還可以對(duì)此路徑進(jìn)行后續(xù)的操作,會(huì)很方便。
判斷路徑是否存在:
使用 os:
- In [18]: os.path.exists("/Users/aaron/tmp")
 - Out[18]: True
 
使用 pathlib:
- In [21]: tmp = Path("/Users/aaron/tmp")
 - In [22]: tmp.exists()
 - Out[22]: True
 
可以看出 pathlib 更易讀,更面向?qū)ο蟆?/p>
顯示文件夾的內(nèi)容
- In [38]: os.listdir("/Users/aaron/tmp")
 - Out[38]: ['.DS_Store', '.hypothesis', 'b.txt', 'a.txt', 'c.py', '.ipynb_checkpoints']
 - In [39]: tmp.iterdir()
 - Out[39]: <generator object Path.iterdir at 0x7fa3f20d95f0>
 - In [40]: list(tmp.iterdir())
 - Out[40]:
 - [PosixPath('/Users/aaron/tmp/.DS_Store'),
 - PosixPath('/Users/aaron/tmp/.hypothesis'),
 - PosixPath('/Users/aaron/tmp/b.txt'),
 - PosixPath('/Users/aaron/tmp/a.txt'),
 - PosixPath('/Users/aaron/tmp/c.py'),
 - PosixPath('/Users/aaron/tmp/.ipynb_checkpoints')]
 
可以看出 Path().iterdir 返回的是一個(gè)生成器,這在目錄內(nèi)文件特別多的時(shí)候可以大大節(jié)省內(nèi)存,提升效率。
通配符支持
os 不支持含有通配符的路徑,但 pathlib 可以:
- In [45]: list(Path("/Users/aaron/tmp").glob("*.txt"))
 - Out[45]: [PosixPath('/Users/aaron/tmp/b.txt'), PosixPath('/Users/aaron/tmp/a.txt')]
 
便捷的讀寫(xiě)文件操作
這是 pathlib 特有的:
- f = Path('test_dir/test.txt'))
 - f.write_text('This is a sentence.')
 - f.read_text()
 
也可以使用 with 語(yǔ)句:
- >>> p = Path('setup.py')
 - >>> with p.open() as f: f.readline()
 - ...
 - '#!/usr/bin/env python3\n'
 
獲取文件的元數(shù)據(jù)
- In [56]: p = Path("/Users/aaron/tmp/c.py")
 - In [57]: p.stat()
 - Out[57]: os.stat_result(st_mode=33188, st_ino=35768389, st_dev=16777221, st_nlink=1, st_uid=501, st_gid=20, st_size=20, st_atime=1620633580, st_mtime=1620633578, st_ctime=1620633578)
 - In [58]: p.parts
 - Out[58]: ('/', 'Users', 'aaron', 'tmp', 'c.py')
 - In [59]: p.parent
 - Out[59]: PosixPath('/Users/aaron/tmp')
 - In [60]: p.resolve()
 - Out[60]: PosixPath('/Users/aaron/tmp/c.py')
 - In [61]: p.exists()
 - Out[61]: True
 - In [62]: p.is_dir()
 - Out[62]: False
 - In [63]: p.is_file()
 - Out[63]: True
 - In [64]: p.owner()
 - Out[64]: 'aaron'
 - In [65]: p.group()
 - Out[65]: 'staff'
 - In [66]: p.name
 - Out[66]: 'c.py'
 - In [67]: p.suffix
 - Out[67]: '.py'
 - In [68]: p.suffixes
 - Out[68]: ['.py']
 - In [69]: p.stem
 - Out[69]: 'c'
 
路徑的連接 join
相比 os.path.join,使用一個(gè) / 是不是更為直觀和便捷?
- >>> p = PurePosixPath('foo')
 - >>> p / 'bar'
 - PurePosixPath('foo/bar')
 - >>> p / PurePosixPath('bar')
 - PurePosixPath('foo/bar')
 - >>> 'bar' / p
 - PurePosixPath('bar/foo')
 
當(dāng)然,也可以使用 joinpath 方法
- >>> PurePosixPath('/etc').joinpath('passwd')
 - PurePosixPath('/etc/passwd')
 - >>> PurePosixPath('/etc').joinpath(PurePosixPath('passwd'))
 - PurePosixPath('/etc/passwd')
 - >>> PurePosixPath('/etc').joinpath('init.d', 'apache2')
 - PurePosixPath('/etc/init.d/apache2')
 - >>> PureWindowsPath('c:').joinpath('/Program Files')
 - PureWindowsPath('c:/Program Files')
 
路徑匹配
- >>> PurePath('a/b.py').match('*.py')
 - True
 - >>> PurePath('/a/b/c.py').match('b/*.py')
 - True
 - >>> PurePath('/a/b/c.py').match('a/*.py')
 - False
 
pathlib 出現(xiàn)的背景和要解決的問(wèn)題
pathlib 目的是提供一個(gè)簡(jiǎn)單的類(lèi)層次結(jié)構(gòu)來(lái)處理文件系統(tǒng)的路徑,同時(shí)提供路徑相關(guān)的常見(jiàn)操作。那為什么不使用 os 模塊或者 os.path 來(lái)實(shí)現(xiàn)呢?
許多人更喜歡使用 datetime 模塊提供的高級(jí)對(duì)象來(lái)處理日期和時(shí)間,而不是使用數(shù)字時(shí)間戳和 time 模塊 API。同樣的原因,假如使用專(zhuān)用類(lèi)表示文件系統(tǒng)路徑,也會(huì)更受歡迎。
換句話(huà)說(shuō),os.path 是面向過(guò)程風(fēng)格的,而 pathlib 是面向?qū)ο箫L(fēng)格的。Python 也在一直在慢慢地從復(fù)制 C 語(yǔ)言的 API 轉(zhuǎn)變?yōu)閲@各種常見(jiàn)功能提供更好,更有用的抽象。
其他方面,使用專(zhuān)用的類(lèi)處理特定的需求也是很有必要的,例如 Windows 路徑不區(qū)分大小寫(xiě)。
在這樣的背景下,pathlib 在 Python 3.4 版本加入標(biāo)準(zhǔn)庫(kù)。
pathlib 的優(yōu)勢(shì)和劣勢(shì)分別是什么
pathlib 的優(yōu)勢(shì)在于考慮了 Windows 路徑的特殊性,同時(shí)提供了帶 I/O 操作的和不帶 I/O 操作的類(lèi),使用場(chǎng)景更加明確,API 調(diào)用更加易懂。
先看下 pathlib 對(duì)類(lèi)的劃分:
圖中的箭頭表示繼承自,比如 Path 繼承自 PurePath,PurePath 表示純路徑類(lèi),只提供路徑常見(jiàn)的操作,但不包括實(shí)際 I/O 操作,相對(duì)安全;Path 包含 PurePath 的全部功能,包括 I/O 操作。
PurePath 有兩個(gè)子類(lèi),一個(gè)是 PureWindowsPath,表示 Windows 下的路徑,不區(qū)分大小寫(xiě),另一個(gè)是 PurePosixPath,表示其他系統(tǒng)的路徑。有了 PureWindowsPath,你可以這樣對(duì)路徑進(jìn)行比較:
- from pathlib import PureWindowsPath
 - >>> PureWindowsPath('a') == PureWindowsPath('A')
 - True
 
PurePath 可以在任何操作系統(tǒng)上實(shí)例化,也就是說(shuō)與平臺(tái)無(wú)關(guān),你可以在 unix 系統(tǒng)上使用 PureWindowsPath,也可以在 Windows 系統(tǒng)上使用 PurePosixPath,他們還可以相互比較。
- >>> from pathlib import PurePosixPath, PureWindowsPath, PosixPath
 - >>> PurePosixPath('a') == PurePosixPath('b')
 - False
 - >>> PurePosixPath('a') < PurePosixPath('b')
 - True
 - >>> PurePosixPath('a') == PosixPath('a')
 - True
 - >>> PurePosixPath('a') == PureWindowsPath('a')
 - False
 
可以看出,同一個(gè)類(lèi)可以相互比較,不同的類(lèi)比較的結(jié)果是 False。
相反,包含 I/O 操作的類(lèi) PosixPath 及 WindowsPath 只能在對(duì)應(yīng)的平臺(tái)實(shí)例化:
- In [8]: from pathlib import PosixPath,WindowsPath
 - In [9]: PosixPath('a')
 - Out[9]: PosixPath('a')
 - In [10]: WindowsPath('a')
 - ---------------------------------------------------------------------------
 - NotImplementedError Traceback (most recent call last)
 - <ipython-input-10-cc7a0d86d4ed> in <module>
 - ----> 1 WindowsPath('a')
 - /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pathlib.py in __new__(cls, *args, **kwargs)
 - 1038 self = cls._from_parts(args, init=False)
 - 1039 if not self._flavour.is_supported:
 - -> 1040 raise NotImplementedError("cannot instantiate %r on your system"
 - 1041 % (cls.__name__,))
 - 1042 self._init()
 - NotImplementedError: cannot instantiate 'WindowsPath' on your system
 - In [11]:
 
要說(shuō)劣勢(shì),如果有的話(huà),那就是在選擇類(lèi)時(shí)會(huì)比較困惑,到底用哪一個(gè)呢?其實(shí)如果你不太確定的話(huà),用 Path 就可以了,這也是它的名稱(chēng)最短的原因,因?yàn)楦映S?,短點(diǎn)的名稱(chēng)編寫(xiě)的更快。
適用的場(chǎng)景
如果要處理文件系統(tǒng)相關(guān)的操作,選 pathlib 就對(duì)了。
一些關(guān)鍵點(diǎn)
獲取家目錄:
- In [70]: from pathlib import Path
 - In [71]: Path.home()
 - Out[71]: PosixPath('/Users/aaron')
 
父目錄的層級(jí)獲?。?/p>
- >>> p = PureWindowsPath('c:/foo/bar/setup.py')
 - >>> p.parents[0]
 - PureWindowsPath('c:/foo/bar')
 - >>> p.parents[1]
 - PureWindowsPath('c:/foo')
 - >>> p.parents[2]
 - PureWindowsPath('c:/')
 
獲取多個(gè)文件后綴:
- >>> PurePosixPath('my/library.tar.gar').suffixes
 - ['.tar', '.gar']
 - >>> PurePosixPath('my/library.tar.gz').suffixes
 - ['.tar', '.gz']
 - >>> PurePosixPath('my/library').suffixes
 - []
 
Windows 風(fēng)格轉(zhuǎn) Posix:
- >>> p = PureWindowsPath('c:\\windows')
 - >>> str(p)
 - 'c:\\windows'
 - >>> p.as_posix()
 - 'c:/windows'
 
獲取文件的 uri:
- >>> p = PurePosixPath('/etc/passwd')
 - >>> p.as_uri()
 - 'file:///etc/passwd'
 - >>> p = PureWindowsPath('c:/Windows')
 - >>> p.as_uri()
 - 'file:///c:/Windows'
 
判斷是否絕對(duì)路徑:
- >>> PurePosixPath('/a/b').is_absolute()
 - True
 - >>> PurePosixPath('a/b').is_absolute()
 - False
 - >>> PureWindowsPath('c:/a/b').is_absolute()
 - True
 - >>> PureWindowsPath('/a/b').is_absolute()
 - False
 - >>> PureWindowsPath('c:').is_absolute()
 - False
 - >>> PureWindowsPath('//some/share').is_absolute()
 - True
 
文件名若有變化:
- >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
 - >>> p.with_name('setup.py')
 - PureWindowsPath('c:/Downloads/setup.py')
 
是不是非常方便?
技術(shù)的底層原理和關(guān)鍵實(shí)現(xiàn)
pathlib 并不是基于 str 的實(shí)現(xiàn),而是基于 object 設(shè)計(jì)的,這樣就嚴(yán)格地區(qū)分了 Path 對(duì)象和字符串對(duì)象,同時(shí)也用到了一點(diǎn) os 的功能,比如 os.name,os.getcwd 等,這一點(diǎn)大家可以看 pathlib 的源碼了解更多。
最后的話(huà)
本文分享了 pathlib 的用法,后面要處理路徑相關(guān)的操作時(shí),你應(yīng)該第一時(shí)間想到 pathlib,不會(huì)用沒(méi)有關(guān)系,搜索引擎所搜索 pathlib 就可以看到具體的使用方法。
雖然 pathlib 比 os 庫(kù)更高級(jí),更方便并且提供了很多便捷的功能,但是我們?nèi)匀恍枰廊绾问褂?os 庫(kù),因?yàn)?os 庫(kù)是 Python 中功能最強(qiáng)大且最基本的庫(kù)之一,但是,在需要一些文件系統(tǒng)操作時(shí),強(qiáng)烈建議使用 pathlib。
本文轉(zhuǎn)載自微信公眾號(hào)「Python七號(hào)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Python七號(hào)公眾號(hào)。

















 
 
 








 
 
 
 