python模塊導入與模塊包導入

一、 import 如何工作

main.py程序第一次導入指定文件mod.py時,執行:

  1. 找到模塊文件mod.py
  2. 編譯成字節碼(可選)
  3. 執行.pyc字節碼,創建mod.py中定義的對象。

導入模塊後,模塊對象會被存儲在sys.modules的表中。每次導入時,若此表中已存在相關模塊,以上3步會被跳過。

  • 導入只發生1次,重複的import導入無效。因此,重複的import不會再次去執行被導入模塊的語句。
  • 若強制使用reload則除外。
1. 搜索模塊文件

按順序:

  1. 程序主目錄
    包含頂層程序文件(_name_ == ‘_main_’ )的目錄。
  2. PYTHONPATH目錄
    環境變量,以支持跨目錄導入。
  3. 標準庫目錄
    安裝python時標準庫安裝的目錄。
  4. .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個子目錄datamodel包含了不同的模塊文件。
這裏,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 * 導入者隱藏數據
  1. 模塊內的頂層定義 __all__列表變量,以字符串名指定 from * 能導入的變量。
  2. 模塊內的頂層變量名以單下劃線開頭時 _varfrom * 不會導入此變量。

若定義了 __all__,則優先遵循第1條,即使此列表中包含了 _var 的變量,from * 也能導入其。
以上2條規則對 import 無效。

2. 模塊的自帶屬性
  • __file__:模塊文件的絕對路徑 “c:/Users/…/mod1.py”
  • __doc__:模塊文件開頭的說明字符串
  • __name__:python自動設置此屬性,當此文件作爲頂層文件執行,會設置爲 "__main__";作爲被導入的模塊時,會設置成模塊名。模塊可以自己檢測此屬性,判斷自己是被導入還是作爲頂層文件執行。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章