1. 程式人生 > 程式設計 >python標準庫OS模組詳解

python標準庫OS模組詳解

python標準庫OS模組簡介

os就是“operating system”的縮寫,顧名思義,os模組提供的就是各種 Python 程式與作業系統進行互動的介面。通過使用os模組,一方面可以方便地與作業系統進行互動,另一方面頁可以極大增強程式碼的可移植性。如果該模組中相關功能出錯,會丟擲OSError異常或其子類異常。

注意

如果是讀寫檔案的話,建議使用內建函式open();

如果是路徑相關的操作,建議使用os的子模組os.path;

如果要逐行讀取多個檔案,建議使用fileinput模組;

要建立臨時檔案或路徑,建議使用tempfile模組;

要進行更高階的檔案和路徑操作則應當使用shutil模組。

當然,使用os模組可以寫出作業系統無關的程式碼並不意味著os無法呼叫一些特定系統的擴充套件功能,但要切記一點:一旦這樣做就會極大損害程式碼的可移植性。

此外,匯入os模組時還要小心一點,千萬不要為了圖呼叫省事兒而將os模組解包匯入,即不要使用from os import *來匯入os模組;否則os.open()將會覆蓋內建函式open(),從而造成預料之外的錯誤。

2. 常用功能

注意,os模組中大多數接受路徑作為引數的函式也可以接受“檔案描述符”作為引數。

檔案描述符:file descriptor,在 Python 文件中簡記為 fd,是一個與某個開啟的檔案物件繫結的整數,可以理解為該檔案在系統中的編號。

2.1 os.name

該屬性寬泛地指明瞭當前 Python 執行所在的環境,實際上是匯入的作業系統相關模組的名稱。這個名稱也決定了模組中哪些功能是可用的,哪些是沒有相應實現的。

目前有效名稱為以下三個:posix,nt,java。

其中posix是 Portable Operating System Interface of UNIX(可移植作業系統介面)的縮寫。Linux 和 Mac OS 均會返回該值;nt全稱應為“Microsoft Windows NT”,大體可以等同於 Windows 作業系統,因此 Windows 環境下會返回該值;java則是 Java 虛擬機器環境下的返回值。

因此在我的電腦(win10)上執行下述程式碼,返回值是nt:

>>> import os
>>> os.name
'nt'

而在 WSL(Windows Subsystem Linux,Windows 下的 Linux 子系統)上的結果則是:

>>> import os
>>> os.name
'posix'

檢視sys模組中的sys.platform屬性可以得到關於執行平臺更詳細的資訊,在此不再贅述

2.2 os.environ

os.environ屬性可以返回環境相關的資訊,主要是各類環境變數。返回值是一個對映(類似字典型別),具體的值為第一次匯入os模組時的快照;其中的各個鍵值對,鍵是環境變數名,值則是環境變數對應的值。在第一次匯入os模組之後,除非直接修改os.environ的值,否則該屬性的值不再發生變化。

比如其中鍵為“HOMEPATH”(Windows 下,Linux 下為“HOME”)的項,對應的值就是使用者主目錄的路徑。Windows 下,其值為:

>>> os.environ["HOMEPATH"]
'd:\\justdopython'

Linux 下,其值為:

>>> os.environ["HOME"]
'/home/justdopython'

2.3 os.walk()

這個函式需要傳入一個路徑作為top引數,函式的作用是在以top為根節點的目錄樹中游走,對樹中的每個目錄生成一個由(dirpath,dirnames,filenames)三項組成的三元組。

其中,dirpath是一個指示這個目錄路徑的字串,dirnames是一個dirpath下子目錄名(除去“.”和“..”)組成的列表,filenames則是由dirpath下所有非目錄的檔名組成的列表。要注意的是,這些名稱並不包含所在路徑本身,要獲取dirpath下某個檔案或路徑從top目錄開始的完整路徑,需要使用os.path.join(dirpath,name)。

注意最終返回的結果是一個迭代器,我們可以使用for語句逐個取得迭代器的每一項:

>>> for item in os.walk("."):
...   print(item)
...
('.',['do'],['go_go_go.txt'])
('.\\do',['IAmDirectory','python'],[])
('.\\do\\IAmDirectory',[],[])
('.\\do\\python',['hello_justdopython.txt'])

python標準庫OS模組詳解

2.4 os.listdir()

“listdir”即“list directories”,列出(當前)目錄下的全部路徑(及檔案)。該函式存在一個引數,用以指定要列出子目錄的路徑,預設為“.”,即“當前路徑”。

函式返回值是一個列表,其中各元素均為字串,分別是各路徑名和檔名。

通常在需要遍歷某個資料夾中檔案的場景下極為實用。

比如定義以下函式:

def get_filelists(file_dir='.'):
  list_directory = os.listdir(file_dir)
  filelists = []
  for directory in list_directory:
    # os.path 模組稍後會講到
    if(os.path.isfile(directory)):
      filelists.append(directory)
  return filelists

該函式的返回值就是當前目錄下所有檔案而非資料夾的名稱列表。

2.5 os.mkdir()

“mkdir”,即“make directory”,用處是“新建一個路徑”。需要傳入一個類路徑引數用以指定新建路徑的位置和名稱,如果指定路徑已存在,則會丟擲FileExistsError異常。

該函式只能在已有的路徑下新建一級路徑,否則(即新建多級路徑)會丟擲FileNotFoundError異常。

相應地,在需要新建多級路徑的場景下,可以使用os.makedirs()來完成任務。函式os.makedirs()執行的是遞迴建立,若有必要,會分別新建指定路徑經過的中間路徑,直到最後創建出末端的“葉子路徑”。

示例如下:

>>> os.mkdir("test_os_mkdir")
>>> os.mkdir("test_os_mkdir")
Traceback (most recent call last):
 File "<stdin>",line 1,in <module>
FileExistsError: [WinError 183] 當檔案已存在時,無法建立該檔案。: 'test_os_mkdir'
>>> 
>>> os.mkdir("test_os_mkdir/test_os_makedirs/just/do/python/hello")
Traceback (most recent call last):
 File "<stdin>",in <module>
FileNotFoundError: [WinError 3] 系統找不到指定的路徑。: 'test_os_mkdir/test_os_makedirs/just/do/python/hello'
>>> 
>>> os.makedirs("test_os_mkdir/test_os_makedirs/just/do/python/hello")

2.6 os.remove()

用於刪除檔案,如果指定路徑是目錄而非檔案的話,就會丟擲IsADirectoryError異常。刪除目錄應該使用os.rmdir()函式。

同樣的,對應於os.makedirs(),刪除路徑操作os.rmdir()也有一個遞迴刪除的函式os.removedirs(),該函式會嘗試從最下級目錄開始,逐級刪除指定的路徑,幾乎就是一個os.makedirs()的逆過程;一旦遇到非空目錄即停止。

2.7 os.rename()

該函式的作用是將檔案或路徑重新命名,一般呼叫格式為os.rename(src,dst),即將src指向的檔案或路徑重新命名為dst指定的名稱。

注意,如果指定的目標路徑在其他目錄下,該函式還可實現檔案或路徑的“剪下並貼上”功能。但無論直接原地重新命名還是“剪下貼上”,中間路徑都必須要存在,否則就會丟擲FileNotFoundError異常。如果目標路徑已存在,Windows 下會丟擲FileExistsError異常;Linux 下,如果目標路徑為空且使用者許可權允許,則會靜默覆蓋原路徑,否則丟擲OSError異常,

和上兩個函式一樣,該函式也有對應的遞迴版本os.renames(),能夠建立缺失的中間路徑。

注意,這兩種情況下,如果函式執行成功,都會呼叫os.removedir()函式來遞迴刪除源路徑的最下級目錄。

2.8 os.getcwd()

“getcwd”實際上是“get the current working directory”的簡寫,顧名思義,也就是說這個函式的作用是“獲取當前工作路徑”。在程式執行的過程中,無論物理上程式在實際儲存空間的什麼地方,“當前工作路徑”即可認為是程式所在路徑;與之相關的“相對路徑”、“同目錄下模組匯入”等相關的操作均以“當前工作路徑”為準。

在互動式環境中,返回的就是互動終端開啟的位置;而在 Python 檔案中,返回的則是檔案所在的位置。

在 Windows 下會有如下輸出:

>>> os.getcwd()
'd:\\justdopython\\just\\do\\python'

Linux 下的輸出則是:

>>> os.getcwd()
'/home/justdopython/just/do/python'

2.9 os.chdir()

“chdir”其實是“change the directory”的簡寫,因此os.chdir()的用處實際上是切換當前工作路徑為指定路徑。其中“指定路徑”需要作為引數傳入函式os.chdir(),該引數既可以是文字或位元組型字串,也可以是一個檔案描述符,還可以是一個廣義的類路徑(path-like)物件。若指定路徑不存在,則會丟擲FileNotFoundError異常。

在 Windows 下,呼叫該函式的效果為:

>>> os.chdir("d:/justdopython/just/do")
>>> os.getcwd()
'd:\\justdopython\\just\\do'

在 Linux 下的效果則是:

>>> os.chdir("/home/justdopython/just/do") # 也可將引數指定為"..",即可切換到父目錄
>>> os.getcwd()
'/home/justdopython/just/do'

有了這個函式,跨目錄讀寫檔案和呼叫模組就會變得非常方便了,很多時候也就不必再反覆將同一個檔案在各個目錄之間複製貼上執行,指令碼完全可以坐鎮中軍,在一個目錄下完成對其他目錄檔案的操作,正所謂“運籌帷幄之中,決勝於千里之外”也。

舉例來說,可以通過將“當前工作目錄”切換到父目錄,從而直接訪問父目錄的檔案內容:

>>> os.chdir("..")
>>> os.getcwd()
'D:\\justdopython\\just'
>>> with open("hello_justdopython.txt",encoding="utf-8") as f:
...   f.read()
...
'歡迎訪問 justdopython.com,一起學習 Python 技術~'
>>> os.listdir()
['hello_justdopython.txt']

3. os.path 模組

其實這個模組是os模組根據系統型別從另一個模組匯入的,並非直接由os模組實現,比如os.name值為nt,則在os模組中執行import ntpath as path;如果os.name值為posix,則匯入posixpath。

使用該模組要注意一個很重要的特性:os.path中的函式基本上是純粹的字串操作。換句話說,傳入該模組函式的引數甚至不需要是一個有效路徑,該模組也不會試圖訪問這個路徑,而僅僅是按照“路徑”的通用格式對字串進行處理。

更進一步地說,os.path模組的功能我們都可以自己使用字串操作手動實現,該模組的作用是讓我們在實現相同功能的時候不必考慮具體的系統,尤其是不需要過多關注檔案系統分隔符的問題。

3.1 os.path.join()

這是一個十分實用的函式,可以將多個傳入路徑組合為一個路徑。實際上是將傳入的幾個字串用系統的分隔符連線起來,組合成一個新的字串,所以一般的用法是將第一個引數作為父目錄,之後每一個引數即使下一級目錄,從而組合成一個新的符合邏輯的路徑。

但如果傳入路徑中存在一個“絕對路徑”格式的字串,且這個字串不是函式的第一個引數,那麼其他在這個引數之前的所有引數都會被丟棄,餘下的引數再進行組合。更準確地說,只有最後一個“絕對路徑”及其之後的引數才會體現在返回結果中。

>>> os.path.join("just","do","python","dot","com")
'just\\do\\python\\dot\\com'
>>> 
>>> os.path.join("just","d:/","com")
'd:/python\\dot\\com'
>>> 
>>> os.path.join("just","g:/","com")
'g:/com'

3.2 os.path.abspath()

將傳入路徑規範化,返回一個相應的絕對路徑格式的字串。

也就是說當傳入路徑符合“絕對路徑”的格式時,該函式僅僅將路徑分隔符替換為適應當前系統的字元,不做其他任何操作,並將結果返回。所謂“絕對路徑的格式”,其實指的就是一個字母加冒號,之後跟分隔符和字串序列的格式:

>>> os.path.abspath("a:/just/do/python")
'a:\\just\\do\\python'
>>> # 我的系統中並沒有 a 盤

當指定的路徑不符合上述格式時,該函式會自動獲取當前工作路徑,並使用os.path.join()函式將其與傳入的引數組合成為一個新的路徑字串。示例如下:

>>> os.path.abspath("ityouknow")
'D:\\justdopython\\ityouknow'

3.3 os.path.basename()

該函式返回傳入路徑的“基名”,即傳入路徑的最下級目錄。

>>> os.path.basename("/ityouknow/justdopython/IAmBasename")
'IAmBasename'
>>> # 我的系統中同樣沒有這麼一個路徑。可見 os.path.basename() 頁是單純進行字串處理

整這個函式要注意的一點是,返回的“基名”實際上是傳入路徑最後一個分隔符之後的子字串,也就是說,如果最下級目錄之後還有一個分隔符,得到的就會是一個空字串:

>>> os.path.basename("/ityouknow/justdopython/IAmBasename/")
''

3.4 os.path.dirname()

與上一個函式正好相反,返回的是最後一個分隔符前的整個字串:

>>> os.path.dirname("/ityouknow/justdopython/IAmBasename")
'/ityouknow/justdopython'
>>> 
>>> os.path.dirname("/ityouknow/justdopython/IAmBasename/")
'/ityouknow/justdopython/IAmBasename'

3.5 os.path.split()

哈哈實際上前兩個函式都是弟弟,這個函式才是老大。

函式os.path.split()的功能就是將傳入路徑以最後一個分隔符為界,分成兩個字串,並打包成元組的形式返回;前兩個函式os.path.dirname()和os.path.basename()的返回值分別是函式os.path.split()返回值的第一個、第二個元素。就連二者的具體實現都十分真實:

def basename(p):
  """Returns the final component of a pathname"""
  return split(p)[1]
def dirname(p):
  """Returns the directory component of a pathname"""
  return split(p)[0]

通過os.path.join()函式又可以把它們組合起來得到原先的路徑。

3.6 os.path.exists()

這個函式用於判斷路徑所指向的位置是否存在。若存在則返回True,不存在則返回False:

>>> os.path.exists(".")
True
>>> os.path.exists("./just")
True
>>> os.path.exists("./Inexistence") # 不存在的路徑
False

一般的用法是在需要持久化儲存某些資料的場景,為避免重複建立某個檔案,需要在寫入前用該函式檢測一下相應檔案是否存在,若不存在則新建,若存在則在檔案內容之後增加新的內容。

3.7 os.path.isabs()

該函式判斷傳入路徑是否是絕對路徑,若是則返回True,否則返回False。當然,僅僅是檢測格式,同樣不對其有效性進行任何核驗:

>>> os.path.isabs("a:/justdopython")
True

3.8 os.path.isfile() 和 os.path.isdir()

這兩個函式分別判斷傳入路徑是否是檔案或路徑,注意,此處會核驗路徑的有效性,如果是無效路徑將會持續返回False。

>>> # 無效路徑
>>> os.path.isfile("a:/justdopython")
False
>>> 
>>> # 有效路徑
>>> os.path.isfile("./just/plain_txt")
True
>>> 
>>> # 無效路徑
>>> os.path.isdir("a:/justdopython/")
False
>>> # 有效路徑
>>> os.path.isdir("./just/")
True

4. 總結

本文詳細介紹了與作業系統互動的os模組中一些常用的屬性和函式,基本可以覆蓋初階的學習和使用。有了這些功能,我們已經可以寫出一些比較實用的指令碼了。

除了文中介紹的函式外,os模組還有很多更加複雜的功能,但大多是我們暫時用不到的,以後用到會進一步講解。

本文主要講解了python標準庫OS模組詳細使用方法,更多關於python標準庫OS模組知識請檢視下面的相關連結