1. pathlib文件系統路徑作為對象
pathlib模塊提供了一個面向對象API來解析、建立、測試和處理文件名和路徑,而不是使用底層字符串操作。
1.1 路徑表達
pathlib包含一些類來管理使用POSIX標准或Microsoft Windows語法格式化的文件系統路徑。這個模塊包含一些“純”類,會處理字符串但不與實際的文件系統交互,另外還包含一些“具體”類,它們擴展了API,以包含可以反映或修改本地文件系統上數據的操作。
純類PurePosixPath和PureWindowsPath可以在任意操作系統上實例化和使用,因為它們只處理文件名和目錄名。要實例化一個具體類來處理真正的文件系統,需要使用Path得到一個PosixPath或WindowsPath,這取決於具體的平台。
1.2 建立路徑
要實例化一個新路徑,可以提供一個字符串作為第一個參數。路徑對象的字符串表示就是這個名值。要創建一個新路徑來指定相對於已有路徑的一個值,可以使用/操作符擴展這個路徑。這個操作符的參數可以是一個字符串,也可以是另一個路徑對象。
import pathlib usr = pathlib.PurePosixPath('/usr') print(usr) usr_local = usr / 'local' print(usr_local) usr_share = usr / pathlib.PurePosixPath('share') print(usr_share) root = usr / '..' print(root) etc = root / '/etc/' print(etc)
如示例輸出中root的值所示,這個操作符會組合所提供的路徑值,但是在包含父目錄引用“..”時沒有對結果進行規范化。不過,如果在一個路徑段以路徑分隔符開頭,那么與os.path.join()中一樣,會把它解釋為一個新的“根”引用。會從路徑值中間刪除額外的路徑分隔符,如這里的etc示例所示。
具體路徑類包括一個用於規范化路徑resolve()方法,它會在文件系統中查找目錄和符號鏈接,並生成一個名指示的絕對路徑。
import pathlib usr_local = pathlib.Path('/usr/local') share = usr_local / '..' / 'share' print(share.resolve())
這里相對路徑被轉換為絕對路徑E:\usr\shar。如果輸入路徑包含符號鏈接,那么它們也會展開,以使規范化的路徑直接指示目標。
要想在路徑段提前不可知時建立路徑,可以使用joinpath(),並傳入各個路徑段作為一個單獨的參數。
import pathlib root = pathlib.PurePosixPath('/') subdirs = ['usr', 'local'] usr_local = root.joinpath(*subdirs) print(usr_local)
與/操作符一樣,調用joinpath()會創建一個新實例。
給定一個現有的路徑對象,可以很容易地建立一個與它稍有差別的新對象,如指示同一個目錄中的另一個文件。可以使用with_name()創建一個新路徑,將一個路徑中的部分替換為另一個不同的文件名。使用with_suffix()也可以創建一個新路徑,將文件名的擴展名替換為一個不同的值。
import pathlib ind = pathlib.PurePosixPath('source/pathlib/index.rst') print(ind) py = ind.with_name('pathlib_from_existing.py') print(py) pyc = py.with_suffix('.pyc') print(pyc)
這兩個方法都返回新對象,原來的對象仍保持不變。
1.3 解析路徑
路徑對象提供了一些方法和屬性可以從路徑名中抽取出部分值。例如,parts屬性可以生成根據路徑分隔符解析得到的一個路徑段序列。
路徑對象具有從名稱中提取部分值的方法和屬性。例如,parts屬性可以生成根據路徑分隔符值解析得到的一個路徑段序列。
import pathlib p = pathlib.PurePosixPath('/usr/local') print(p.parts)
這個序列是一個元組,反映了路徑實例的不可變性。
有兩種方法可以從一個給定的路徑對象在文件系統層次結構中“向上”導航。parent屬性指示一個新的路徑實例,對應包含給定路徑(os.path.dirname()返回的值)的目錄。parents屬性是一個迭代器,會生成一系列父目錄引用,在路徑層次結構中不斷“向上”,直到到達文件系統的根目錄。
import pathlib p = pathlib.PurePosixPath('/usr/local/lib') print('parent: {}'.format(p.parent)) print('\nhierarchy:') for up in p.parents: print(up)
這個例子迭代處理parents屬性並打印成員值。
可以通過路徑對象的屬性來訪問路徑的其他部分。Name屬性包含路徑的最后一部分,即最后一個路徑分隔符后面的部分(與os.path.basename()生成的值相同)。suffix屬性包含擴展名分隔符后面的值,stem屬性包含名字中后綴之前的部分。
import pathlib p = pathlib.PurePosixPath('./source/pathlib/pathlib_name.py') print('path : {}'.format(p)) print('name : {}'.format(p.name)) print('suffix: {}'.format(p.suffix)) print('stem : {}'.format(p.stem))
盡管suffix和stem的值與os.path.splitext()生成的值類似,但這些值只是基於name的值,而不是完整路徑。
1.4 創建具體路徑
可以由字符串參數創建具體Path類的實例,字符串參數可能指示文件系統中一個文件、目錄或符號鏈接的名字(或可能的名字)。這個類還提供了很多便利方法,可以使用常用位置(如當前工作目錄和用戶的主目錄)建立路徑實例,這些常用位置可能會改變。
import pathlib home = pathlib.Path.home() print('home: ', home) cwd = pathlib.Path.cwd() print('cwd : ', cwd)
這兩個方法都會創建預填充一個絕對文件系統引用的Path實例。
1.5 目錄內容
可以使用3個方法來訪問目錄列表以及發現文件系統中的文件名。iterdir()是一個生成器,會為包含目錄中的每個元素生成一個新的path實例。
import pathlib p = pathlib.Path('.') for f in p.iterdir(): print(f)
如果Path不指示一個目錄,則iterdir()會產生NotADirectoryError。
可以使用glob()找出與一個模式匹配的文件。
import pathlib p = pathlib.Path('..') for f in p.glob('*.py'): print(f)
這個例子展示了腳本父目錄中的所有py文件。
glob處理器支持使用模式前綴**或者通過調用rglob()而不是glob()來完成遞歸掃描。
import pathlib p = pathlib.Path('..') for f in p.rglob('*.py'): print(f)
由這個例子從父目錄開始,所以必須通過一個遞歸搜索來查找與*.py匹配的示例文件。
1.6 讀寫文件
每個Path實例都包含一些方法來處理所指示文件的內容。要直接獲取內容,可以使用read_bytes()或read_text()。要寫入文件,可以使用write_bytes()或write_text()。可以使用open()方法打開文件並保存文件句柄,而不是向內置的open()函數傳入文件名。
import pathlib f = pathlib.Path('example.txt') f.write_bytes('This is the content'.encode('utf-8')) with f.open('r', encoding='utf-8') as handle: print('read from open(): {!r}'.format(handle.read())) print('read_text(): {!r}'.format(f.read_text('utf-8')))
這些便利方法會在打開文件和寫入文件之前完成一些類型檢查,除此之外,它們與直接操作是等價的。
1.7 管理目錄和符號鏈接
可以用表示不存在的目錄或符號鏈接的路徑來創建關聯的文件系統項。
import pathlib p = pathlib.Path('example_dir') print('Creating {}'.format(p)) p.mkdir()
如果這個路徑已經存在,則mkdir()會產生一個FileExistsError。
可以使用symlink_to()創建一個符號鏈接。這個鏈接根據路徑的值命名,將指示symlink_to()參數給定的名字。
import pathlib p = pathlib.Path('example_link') p.symlink_to('index.rst') print(p) print(p.resolve().name)
這個例子首先創建一個符號鏈接,然后使用resolve()讀取這個鏈接來找出它指示的名字,並打印這個名字。
1.8 文件類型
Path實例包含一些方法來檢查路徑指示的文件的類型。下面這個例子測試它們。
import pathlib p = pathlib.Path('demo.py') hfmt = '{:18s}' + (' {:>5}' * 6) print(hfmt.format('Name', 'File', 'Dir', 'Link', 'FIFO', 'Block', 'Character')) print() fmt = '{:20s} ' + ('{!r:>5} ' * 6) print(fmt.format( str(p), p.is_file(), p.is_dir(), p.is_symlink(), p.is_fifo(), p.is_block_device(), p.is_char_device(), ))
所有這些方法(is_dir()、is_file()、is_symlink()、is_fifo()、is_block_device()和is_char_device())都不帶參數。
1.9 文件屬性
可以使用方法stat()和lstat()來訪問文件的有關詳細信息(lstat()用於檢查一個可能是符號鏈接的目標的狀態)。這些方法生成的結果分別與os.stat()和os.lstat()相同。
import pathlib import sys import time if len(sys.argv) == 1: filename = __file__ else: filename = sys.argv[1] p = pathlib.Path(filename) stat_info = p.stat() print('{}:'.format(filename)) print(' Size:', stat_info.st_size) print(' Permissions:', oct(stat_info.st_mode)) print(' Owner:', stat_info.st_uid) print(' Device:', stat_info.st_dev) print(' Created :', time.ctime(stat_info.st_ctime)) print(' Last modified:', time.ctime(stat_info.st_mtime)) print(' Last accessed:', time.ctime(stat_info.st_atime))
取決於在哪里安裝這個示例代碼,輸出可能有變化。
1.10 刪除
提供了兩個方法來刪除文件系統中的對象,使用哪一個方法取決於具體的類型。要刪除一個空目錄,可以使用rmdir()。
import pathlib p = pathlib.Path('example_dir') print('Removing {}'.format(p)) p.rmdir()
如果后置推薦已經滿足而目錄不存在,則會產生一個FileNotFoundError異常。如果試圖刪除一個不為空的目錄,則也會出現錯誤。
對於文件、符號鏈接和大多數其他路徑類型,可以使用unlink()。
import pathlib p = pathlib.Path('touched') p.touch() print('exists before removing:', p.exists()) p.unlink() print('exists after removing:', p.exists())
用戶必須有刪除文件、符號鏈接、套接字或其他文件系統對象的權限。