解析Python3中的Import
Python import的搜尋路徑
import的搜尋路徑為:
- 搜尋「內建模組」(built-in module)
- 搜尋 sys.path 中的路徑
- 而sys.path在初始化時,又會按照順序新增以下路徑:
foo.py 所在目錄(如果是軟連結,那麼是真正的 foo.py 所在目錄)或當前目錄;
環境變數 PYTHONPATH中列出的目錄(類似環境變數 PATH,由使用者定義,預設為空);
site 模組被 import 時新增的路徑1(site 會在執行時被自動 import)。
import site 所新增的路徑一般是 XXX/site-packages。如果懶得記 sys.path 的初始化過程,可以簡單的認為 import 的查詢順序是:
內建模組
.py 檔案所在目錄
pip 或 easy_install 安裝的包
絕對匯入和相對匯入
絕對匯入和相對匯入的關係可以類比絕對路徑和相對路徑。
絕對匯入的格式為:
import A.B 或 from A import B
相對匯入格式為:
from . import B 或 from ..A import B
其中,點號.代表當前模組,..代表上層模組,…代表上上層模組,依次類推。
模組的執行方式
模組的執行可以有兩種方式:直接執行和以模組執行,即:
python example/foo.py 或 python -m example.foo
注意,以模組執行時,一定要有包的概念,即example一定是個包,而foo是這個包下的模組,這樣才能順利執行。
包和模組
模組: 一個 .py 檔案就是一個模組(module)
包:init .py 檔案所在目錄就是包(package)
各種情形測試
模組直接匯入
即模組所在的目錄都不是一個包結構,各個模組都是獨立的,比如以下的目錄結構:
D:\LEARN\IMPORT_TEST\TEST1 ├─pack1 │ modu1.py └─pack2 modu2.py
modu1.py中的內容為:
import sys sys.path.append("D:\\learn\\import_test\\TEST1\\pack2") from modu2 import hello2 hello2()
modu2.py中的內容為:
def hello2(): print("hello,I am module 2")
注意在modu1中一定加上sys.path.append那部分內容,即根據上面的描述,一定要讓modu1能找到modu2才行,否則就會出現如下錯誤:
ModuleNotFoundError: No module named 'modu2'
此時進入pack1目錄下,以直接執行或模組執行的方式都可以順利輸出。
包外匯入
將上面兩個模組所在的目錄都變為包結構,即:
D:\LEARN\IMPORT_TEST\TEST2 ├─pack1 │ modu1.py │ __init__.py └─pack2 modu2.py __init__.py
此時也能順利執行,同時比上面非包結構的多出來一條執行方式,即:
python -m pack1.modu1
即以包名+模組名的方式執行。
上面兩種情形,即模組與模組、包與包都是相互獨立的關係,也就沒有相對匯入的意義。
如果是在一個包內的不同模組的匯入,那麼最自然的就是使用相對匯入。
包內相對匯入
D:\LEARN\IMPORT_TEST\Test3 │ __init__.py │ ├─pack1 │ modu1.py │ __init__.py │ └─pack2 modu2.py __init__.py
此時modu1.py中的內容為:
from ..pack2.modu2 import hello2 hello2()
即將sys.path.append去掉,因為是在一個包內相互引用,此時這樣寫沒有意義。
此時正確執行的方式是進入Test3上一層的資料夾,然後:
python -m Test3.pack1.modu1
即明確地告訴直譯器模組的層次結構。
而如果採用直接執行的方式,比如:
python Test3\pack1\modu1.py
就會報如下錯誤:
ValueError: attempted relative import beyond top-level package
這是因為,相對匯入使用模組的name (這裡的name和下面的main都是有兩個下劃線的,但是網頁顯示不出來。。)屬性來決定模組在包結構中的位置。當name 屬性不包含包資訊(i.e. 沒有用'.'表示的層次結構,比如'main ‘),則相對匯入將模組解析為頂層模組,而不管模組在檔案系統中的實際位置。這裡模組被直接執行,則它自己為頂層模組,不存在層次結構,所以找不到其他的相對路徑。
因此,直接執行帶有相對匯入的模組是不行的,需要通過模組執行的方式,將包結構明確告訴它才行。
這個原理也適用於下面這種錯誤,比如將modu2移動到pack1中,即與modu1在同一個目錄下,然後將modu1的內容改為這樣的相對引用:
from .modu2 import hello2 hello2()
此時使用模組執行的方式沒有問題,如果還是想嘗試直接執行,那麼就會出現:
ModuleNotFoundError: No module named '__main__.modu2'; '__main__' is not a package
原因就是此時沒有包結構,main 也不是個包。
那麼解決方法就是或者使用模組執行的方式執行,或者將它改成下面的絕對匯入的方式就可以直接執行。
包內絕對匯入
那麼,如果將modu1.py中的內容改為絕對匯入,即:
from Test3.pack2.modu2 import hello2 hello2()
此時正確執行方式也是進入Test3上一層資料夾,然後使用模組執行的方式執行:
python -m Test3.pack1.modu1
如果此時採用直接執行的方式:
python Test3\pack1\modu1.py
那麼就會報錯:
ModuleNotFoundError: No module named 'Test3'
這主要是因為Test3沒有被找到,即按照第一部分所說,Test3沒有在import的搜尋路徑中。所以,只要將它加入進去即可,比如:
set PYTHONPATH=D:\learn\import_test\
此時再直接執行就沒有問題了。
總結
以上所述是小編給大家介紹的Python3中的Import理解,希望對大家有所幫助!