充分理解 python -m mod

藕絲空間發表於2019-02-16

最近在看 __main__ 的官方文件 —— https://docs.python.org/3/library/__main__.html#module-__main__,提到一個 python -m 的用法,很是不理解,所以查詢了很多文件,並進行了如下總結。

檢視 Python 的 help 內容

$ python --help
...
-m mod : run library module as a script (terminates option list)
# 直譯就是按照指令碼的方式執行模組
...

有無 -m 引數的比較

1. python script.py # 直接執行指令碼
2. python -m script.py # 當做模組的方式執行,相當於 import

一般情況下,我們會想當然的認為,有無引數 -m 對程式會有不同的影響,但是很可惜,第二種寫法是會在程式執行之後報錯的。其正確的寫法是:丟掉 .py 字尾

首先直觀的觀察執行結果,Python 的版本是 3.6.3
編寫 script.py 指令碼,內容如下:

import sys
print(sys.path)
print(sys.modules)

直接執行指令碼

$ python script.py
[`/home/ossifrage/workspaces/python-m`, `/usr/lib/python36.zip`, `/usr/lib/python3.6`, ...]
{..., `__main__`: <module `__main__` from `script.py`>, ...}

當做模組方式執行

$ python -m script
[``, `/usr/lib/python36.zip`, `/usr/lib/python3.6`, ...]
{..., `__main__`: <module `run` from `/home/ossifrage/workspaces/python-m/script.py`>, ..., `runpy`: <module `runpy` from `/usr/lib/python3.6/runpy.py`>, `pkgutil`: <module `pkgutil` from `/usr/lib/python3.6/pkgutil.py`>}

顯示的內容還有很多,只突出顯示出了不同的地方。

通過比較不同的地方,我們發現:

第一種方法——直接執行指令碼,當前指令碼所在的路徑會加入到 sys.path 列表中,但是 sys.modules 字典中的 __main__ 的路徑不是絕對路徑,只是指令碼名稱

第二種方法——當做模組方式執行,當前指令碼所在的路徑不會加入到 sys.path 列表中,但是 sys.modules 字典中的 __main__ 的路徑是絕對路徑,同時,還引入了 runpypkgutil 兩個模組

runpy 的用途:定位並執行該模組。主要用途在於實現命令列 -m 執行 python 模組的效果。

pkgutil 的用途:獲取包裡面的所有模組列表。pkgutil.get_data() 可讀取包內任何檔案內容。

模組的匯入機制

其實,上面的內容涉及到了 Python 包模組的匯入機制。

要理解模組的匯入機制,得理解幾個關鍵的名詞。sys.path -> sys.modules -> < module >.__dict__。

sys.path:是一個列表。儲存著模組的搜尋路徑。如果路徑沒有存在與該列表中,可使用 sys.path.append() 匯入。

sys.modules: 是一個字典。所有載入到記憶體中的模組都存放在該字典中。當 import 一個模組的時候,首先會在這個字典中查詢是否已經載入了目標模組。如果已載入,則將模組的名字加入到正在呼叫 import 的模組的 Local 名稱空間(也就是< module >.__dict__)中。如果沒有,則從 sys.path 查詢,找到後載入記憶體,並加入到 sys.modules 字典,名稱也將匯入到當前模組的 Local 名稱空間。

reference

相關文章