一、 import 如何工作
main.py程序第一次導入指定文件mod.py時,執行:
- 找到模塊文件mod.py
- 編譯成字節碼(可選)
- 執行.pyc字節碼,創建mod.py中定義的對象。
導入模塊後,模塊對象會被存儲在sys.modules的表中。每次導入時,若此表中已存在相關模塊,以上3步會被跳過。
- 導入只發生1次,重複的import導入無效。因此,重複的import不會再次去執行被導入模塊的語句。
- 若強制使用reload則除外。
1. 搜索模塊文件
按順序:
- 程序主目錄
包含頂層程序文件(_name_ == ‘_main_’ )的目錄。 - PYTHONPATH目錄
環境變量,以支持跨目錄導入。 - 標準庫目錄
安裝python時標準庫安裝的目錄。 - .pth文件目錄
文本文件。
內置的sys.path列表,保存了按上述順序加載的路徑,python會按索引順序依次搜索路徑下的模塊文件。可在代碼中,爲此列表添加新的搜索路徑。
2. 編譯字節碼
- 對比mod.py文件和字節碼文件.pyc的時間戳,若字節碼文件舊,生成新的字節碼。若字節碼文件與模塊文件同步,則跳過編譯。
- 若只有字節碼,無源模塊文件,則直接加載字節碼。(可避免發佈源碼)
- 只有文件被import時,編譯成的字節碼文件纔會儲存在硬盤,當前運行文件main.py不會生成.pyc文件,其編譯的字節碼在內部使用後被丟棄。
3. 執行字節碼,創建模塊中的對象
在模塊文件mod.py中,從上至下執行mod.py的頂層語句。若頂層語句做了什麼事情,在Import時就會被執行。函數的定義其實就是賦值,將創建的函數對象賦值給函數名。
二、import 與 from
一個模塊就是一個對象。模塊名爲引用此對象的變量。模塊中定義的對象(全局變量,函數,類等)成爲模塊對象的屬性。import from 是賦值語句,和def那樣,將模塊對象賦值給模塊名變量。
import x
from x import a
from x import *
from語句同樣執行import的3個步驟,額外地,還會使用模塊中同名的變量對被導入的幾個(或全部)變量名進行賦值。此過程與函數的參數傳遞類似。因此,這些導入的變量名與模塊對象的同名屬性共享引用。若所引用對象是可變的,from引入的變量名可修改模塊中的同名變量所引用的可變對象。
import導入模塊對象mod後,可通過爲mod的屬性賦值來修改其屬性。mod.a = 10。
三、模塊包導入
root:
data:
__init__.py
mydata.py
model:
__init__.py
net1.py
net2.py
main.py
對於如上文件目錄關係,整個項目在root
文件夾中,2個子目錄data
和model
包含了不同的模塊文件。
這裏,data和model就是模塊包,即一個包含python代碼的文件夾。
包中可以有__init__.py
文件,包就是一個模塊,又分爲名稱空間模塊、普通模塊。
- 無
__init__.py
:名稱空間模塊。僅提供了包的組織功能,而且它們是目錄,而不像py文件一樣,沒有編寫模塊代碼的地方。換句話說,包現在是目錄文件,而不是真正的模塊文件。 - 有
__init__.py
:普通模塊。它表示讓這個目錄格式的模塊(也就是包)像py文件一樣可以寫模塊代碼,只不過這些模塊代碼是寫入__init__
.py中的。其中也可以無任何代碼,只是讓這個包成爲真正的模塊文件。
每次導入包的時候,如果有__init__.py
文件,將會自動執行這個文件中的代碼,所以也可以認爲__init__.py
是包的初始化文件。在python3.3之前,這個文件必須存在,否則就會報錯,因爲它不認爲目錄是有效的模塊。
包導入格式
# main.py
import data.mydata
import model.net1
from model import net2
from model import * #錯誤,無法導入
在運行頂層模塊main.py時,sys.path的主目錄路徑是 ~/root。要從主目錄下的子目錄(data, model目錄)開始寫起。import data.mydata語句,會先執行data文件夾下的__init__.py
文件,再執行mydata.py
文件。
也可使用 from import 形式,但是需要注意:
使用 from model import * 時,被導入的模塊名要在包model的__init__.py
中控制被導入的包中的子模塊。即在__init__.py
中寫定義全局變量 __all__
:
__all__ = ['net1', 'net2']
列表中的字符串控制了能被導入的包中的子模塊。如果沒有定義此列表,from model import * 將只執行model的__init__.py
文件,而不導入任何其下的子模塊。
絕對路徑導入
當net1.py要導入另一個文件夾中的mydata.py時,可以使用如何形式
import data.mydata
此爲相對路徑導入,前提是頂層模塊(即被運行的當前文件)是main.py,如果直接運行net1.py,net1.py中的如上導入會報錯。
原因是:絕對路徑是指:sys.path中的頂層模塊所在的主目錄,以此目錄爲起點,一直到目標模塊。因此,頂層模塊爲main.py時,sys.path的主目錄是~/root
,接着便是 data
。但是,頂層文件爲net1.py時,主目錄是~/root/model
,其後接data路徑顯然是錯誤的。
相對路徑導入
from .data import mydata
相對路徑僅能使用 from import,不能使用import。不推薦使用相對路徑導入。
四、模塊導入的其他細節
1. 對 from *
導入者隱藏數據
- 模塊內的頂層定義
__all__
列表變量,以字符串名指定from *
能導入的變量。 - 模塊內的頂層變量名以單下劃線開頭時
_var
,from *
不會導入此變量。
若定義了 __all__
,則優先遵循第1條,即使此列表中包含了 _var
的變量,from *
也能導入其。
以上2條規則對 import 無效。
2. 模塊的自帶屬性
__file__
:模塊文件的絕對路徑 “c:/Users/…/mod1.py”__doc__
:模塊文件開頭的說明字符串__name__
:python自動設置此屬性,當此文件作爲頂層文件執行,會設置爲"__main__"
;作爲被導入的模塊時,會設置成模塊名。模塊可以自己檢測此屬性,判斷自己是被導入還是作爲頂層文件執行。