1. 程式人生 > 實用技巧 >python學習(33)----Python 中 -m 的典型用法、原理解析與發展演變(轉)

python學習(33)----Python 中 -m 的典型用法、原理解析與發展演變(轉)

轉自:

https://zhuanlan.zhihu.com/p/91120727

-m 選項的兩種原理解析

看了前面的幾種典型用法,你是否開始好奇:“-m”是怎麼運作的?它是怎麼實現的?

對於“python -m name”,一句話解釋:Python 會檢索sys.path,查詢名字為“name”的模組或者包(含名稱空間包),並將其內容當成“__main__”模組來執行。

1、對於普通模組

以“.py”為字尾的檔案就是一個模組,在“-m”之後使用時,只需要使用模組名,不需要寫出字尾,但前提是該模組名是有效的,且不能是用 C 語言寫成的模組。

在“-m”之後,如果是一個無效的模組名,則會報錯“No module named xxx”。

如果是一個帶字尾的模組,則首先會匯入該模組,然後可能報錯:Error while finding module specification for 'xxx.py' (AttributeError: module 'xxx' has no attribute '__path__'。

對於一個普通模組,有時候這兩種寫法表面看起來是等效的:

兩種寫法都會把定位到的模組腳本當成主程式入口來執行,即在執行時,該指令碼的__name__都是”__main__“,跟 import 匯入方式是不同的。

但它的前提是:在執行目錄中存在著“test.py”,且只有唯一的“test”模組。對於本例,如果換一個目錄執行的話,“python test.py”當然會報找不到檔案的錯誤,然而,“python -m test”卻不會報錯,因為直譯器在遍歷sys.path時可以找到同名的“test”模組,並且執行:

由此差異,我們其實可以總結出“-m”的用法:已知一個模組的名字,但不知道它的檔案路徑,那麼使用“-m”就意味著交給直譯器自行查詢,若找到,則當成指令碼執行。

以前文的“python -m http.server 8000”為例,我們也可以找到“server”模組的絕對路徑,然後執行,儘管這樣會變得很麻煩。

那麼,“-m”方式與直接執行指令碼相比,在實現上有什麼不同呢?

  • 直接執行指令碼時,相當於給出了指令碼的完整路徑(不管是絕對路徑還是相對路徑),直譯器根據檔案系統的查詢機制,定位到該指令碼,然後執行
  • 使用“-m”方式時,直譯器需要在不 import 的情況下,在所有模組名稱空間中查詢,定位到指令碼的路徑,然後執行。為了實現這個過程,直譯器會藉助兩個模組:pkgutilrunpy,前者用來獲取所有的模組列表,後者根據模組名來定位並執行指令碼

2、對於包內模組

如果“-m”之後要執行的是一個包,那麼直譯器經過前面提到的查詢過程,先定位到該包,然後會去執行它的“__main__”子模組,也就是說,在包目錄下需要實現一個“__main__.py”檔案。

換句話說,假設有個包的名稱是“pname”,那麼,“python -m pname”,其實就等效於“python -m pname.__main__”。

仍以前文建立 HTTP 服務為例,“http”是 Python 內建的一個包,它沒有“__main__.py”檔案,所以使用“-m”方式執行時,就會報錯:No module named http.__main__; 'http' is a package and cannot be directly executed。

作為對比,我們可以看看前文提到的 pip,它也是一個包,為什麼“python -m pip”的方式可以使用呢?當然是因為它有“__main__.py”檔案:

“python -m pip”實際上執行的就是這個“__main__.py”檔案,它主要作為一個呼叫入口,呼叫了核心的"pip._internal.main"。

http 包因為沒有一個統一的入口模組,所以採用了“python -m 包.模組”的方式,而 pip 包因為有統一的入口模組,所以加了一個“__main__.py”檔案,最後只需要寫“python -m 包”,簡明直觀。