[Python模組學習]使用linecache模組載入和快取檔案內容
linecache模組
接觸到linecache這個模組是因為前兩天讀attrs原始碼的時候看到內部程式碼引用了這個模組來模擬一個假檔案,帶著一臉疑問順便讀了一下這個模組的原始碼,發現其實也就那麼回事兒,程式碼不多,在這總結一下。
linecache模組可以讀取檔案並將檔案內容快取起來,方便後面多次讀取。這個模組原本被設計用來讀取Python模組的原始碼,所以當一個檔名不在指定路徑下的時候,模組會通過搜尋路徑(search path)來嘗試讀取檔案。
介面
linecache模組的__all__
引數其實只提供了getline/clearcache/checkcache三個介面,但實際上可以使用的不止這些,下面我會對所有介面逐個進行介紹。
linecache.getline(filename, lineno, module_globals=None)
獲取指定檔案的某一行,filename指定檔名、lineno指定行號、module_globals用於指定模組的上下文我也不知道怎麼稱呼,姑且稱為上下文吧,最後一個引數其實到會傳到linecache.updatecache()裡,用於嘗試使用__loader__
載入檔案,一般情況下不會用到最後一個引數,忽略即可。當行號小於一或大於檔案最大行號時函式直接返回空字串。
linecache.clearcache()
清空所有快取,注意是所有。
linecache.checkcache(filename=None)
這個函式用於檢查快取,如果檔案的大小或者修改時間有變化,會把檔案原先的快取刪除,如果檔案是懶載入的則保持不變。當filename為None時檢查快取中的所有檔案。
以下是沒寫進模組__all__
引數的介面。
linecache.lazycache(filename, module_globals)
對指定檔案使用懶載入,啟用懶載入成功的檔案會在實際呼叫獲取內容的介面時才將檔案內容載入進記憶體,使用這個可以避免多餘的檔案IO。返回值為一個布林值,當懶載入成功時返回True,如果檔案內容已經實際載入進記憶體或者載入失敗則返回False。
這個函式的module_globals引數是必填的,其實就是傳入要載入檔案對應模組的上下文。比如載入linecache模組,則傳入linecache.__dict__
vars(linecache)
(目前只想到這兩種方法,當然你要自己構建一個字典傳進去也是可以的)。然後函式會根據上下文獲取__loader__
的get_source函式儲存到快取中。這個函式是在Python3.5後新增的。
linecache.updatecache(filename, module_globals=None)
這個函式是整個模組的核心,用於更新檔案快取並返回檔案內容。函式中間任何一個環節出錯了會返回一個空列表。
對於普通檔案,內部使用tokenize.open()函式用於開啟檔案,檢測檔案的編碼並使用檢測到的編碼開啟檔案,如果缺失編碼預設使用UTF-8。如果給定路徑無法開啟檔案則使用sys.path指定的路徑嘗試載入。如果檔案內容的最後一行不帶\n,會自動在最後一個字元加上\n。
對於懶載入的檔案,則呼叫懶載入時儲存的get_source函式獲取檔案內容。
注意:linecache在開啟檔案之後使用readlines一次性載入所有檔案內容,所以在檔案很多或者檔案太大時會出現問題,所以還是應該謹慎使用。
linecache.getlines(filename, module_globals=None)
獲取檔案所有內容,如果檔案尚未載入或者是懶載入,會呼叫linecache.updatecache()載入檔案內容,如果出現MemoryError則清空快取。linecache.getline()其實內部就是呼叫了這個函式。
linecache.cache
這是一個字典,所有檔案的快取就存在這裡面。字典的Key是你讀取時傳入的filename,Value是一個儲存了檔案大小、修改時間、內容、名字的元組,當檔案為懶載入時則是對應__loader__
的get_source函式。
總結
Python標準庫內建了很多基礎模組,平時不會注意到,但是總有一些別的程式碼會依賴到這些基礎設施,像標準庫裡的pdb和traceback都有用到linecache模組。這裡面其實有不少程式碼是能加以利用的,平時多多讀原始碼,會有驚喜的。
參考資料: