python之import機(jī)制詳解
本文詳述了Python的import機(jī)制,對于理解Python的運行機(jī)制很有幫助!
1.標(biāo)準(zhǔn)import:
Python中所有加載到內(nèi)存的模塊都放在 sys.modules 。當(dāng) import 一個模塊時首先會在這個列表中查找是否已經(jīng)加載了此模塊,如果加載了則只是將模塊的名字加入到正在調(diào)用 import 的模塊的 Local 名字空間中。如果沒有加載則從 sys.path 目錄中按照模塊名稱查找模塊文件,模塊可以是py、pyc、pyd,找到后將模塊載入內(nèi)存,并加到 sys.modules 中,并將名稱導(dǎo)入到當(dāng)前的 Local 名字空間。
一個模塊不會重復(fù)載入。多個不同的模塊都可以用 import 引入同一個模塊到自己的 Local 名字空間,其實背后的 PyModuleObject 對象只有一個。這里說一個容易忽略的問題:import 只能導(dǎo)入模塊,不能導(dǎo)入模塊中的對象(類、函數(shù)、變量等)。例如:模塊 A(A.py)中有個函數(shù) getName,另一個模塊不能通過 import A.getName 將 getName導(dǎo)入到本模塊,只能用 from A import getName。
2.嵌套import:
1)順序嵌套
例如:本模塊導(dǎo)入 A 模塊(import A),A 中又 import B,B 模塊又可以 import 其他模塊……
這中嵌套比較容易理解,需要注意的一點就是各個模塊的 Local 名字空間是獨立的。對于上面的例子,本模塊 import A 之后本模塊只能訪問模塊 A,不能訪問模塊 B 及其他模塊。雖然模塊 B 已經(jīng)加載到內(nèi)存了,如果訪問還要再明確的在本模塊中 import B。
2)循環(huán)嵌套
例如:
文件[A.py]
from B import D class C:pass
文件[ B.py ]
from A import C class D:pass
為什么執(zhí)行 A 的時候不能加載 D 呢?
如果將 A.py 改為:import B 就可以了。
這是怎么回事呢?
RobertChen:這跟Python內(nèi)部 import 的機(jī)制是有關(guān)的,具體到 from B import D,Python 內(nèi)部會分成幾個步驟:
(1)在 sys.modules 中查找符號 “B”
(2)如果符號 B 存在,則獲得符號 B 對應(yīng)的 module 對象。
從 <modult B> 的 __dict__ 中獲得符號 “D” 對應(yīng)的對象,如果 “D” 不存在,則拋出異常。
(3)如果符號 B 不存在,則創(chuàng)建一個新的 module 對象 <module B>,注意,此時,module 對象的 __dict__ 為空。
執(zhí)行 B.py 中的表達(dá)式,填充 <module B> 的 __dict__。
從 <module B> 的 __dict__ 中獲得 “D” 對應(yīng)的對象,如果 “D” 不存在,則拋出異常。
所以這個例子的執(zhí)行順序如下:
1、執(zhí)行 A.py 中的 from B import D 由于是執(zhí)行的 python A.py,所以在 sys.modules 中并沒有 <module B> 存在, 首先為 B.py 創(chuàng)建一個 module 對象 (<module B>) , 注意,這時創(chuàng)建的這個 module 對象是空的,里邊啥也沒有, 在 Python 內(nèi)部創(chuàng)建了這個 module 對象之后,就會解析執(zhí)行 B.py,其目的是填充 <module B> 這個 __dict__。
2、執(zhí)行 B.py中的from A import C 在執(zhí)行B.py的過程中,會碰到這一句, 首先檢查sys.modules這個module緩存中是否已經(jīng)存在<module A>了, 由于這時緩存還沒有緩存<module A>, 所以類似的,Python內(nèi)部會為A.py創(chuàng)建一個module對象(<module A>), 然后,同樣地,執(zhí)行A.py中的語句
3、再次執(zhí)行A.py中的from B import D 這時,由于在第1步時,創(chuàng)建的<module B>對象已經(jīng)緩存在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,從整個過程來看,我們知道,這時<module B>還是一個空的對象,里面啥也沒有, 所以從這個module中獲得符號"D"的操作就會拋出異常。 如果這里只是import B,由于"B"這個符號在sys.modules中已經(jīng)存在,所以是不會拋出異常的。
ZQ:圖解如下:

3. 包 import
只要一個文件夾下面有個 __init__.py 文件,那么這個文件夾就可以看做是一個包。包導(dǎo)入的過程和模塊的基本一致,只是導(dǎo)入包的時候會執(zhí)行此包目錄下的 __init__.py 而不是模塊里面的語句了。另外,如果只是單純的導(dǎo)入包,而包的 __init__.py 中又沒有明確的其他初始化操作,那么此包下面的模塊是不會自動導(dǎo)入的。
例如:
有下面的包結(jié)構(gòu):
PA
|---- __init__.py
|---- wave.py
|---- PB1
|---- __init__.py
|---- pb1_m.py
|---- PB2
|---- __init__.py
|---- pb2_m.py
有如下程序:
import sys import PA.wave #1 import PA.PB1 #2 import PA.PB1.pb1_m as m1 #3 import PA.PB2.pb2_m #4 PA.wave.getName() #5 m1.getName() #6 PA.PB.pb2_m.getName() #7
1) 當(dāng)執(zhí)行 #1 后,sys.modules 會同時存在 PA、PA.wave 兩個模塊,此時可以調(diào)用 PA.wave 的任何類或函數(shù)了。但不能調(diào)用 PA.PB1(2) 下的任何模塊。當(dāng)前 Local 中有了 PA 名字。
2) 當(dāng)執(zhí)行 #2 后,只是將 PA.PB1 載入內(nèi)存,sys.modules 中會有 PA、 PA.wave、PA.PB1 三個模塊,但是 PA.PB1 下的任何模塊都沒有自動載入內(nèi)存,此時如果直接執(zhí)行 PA.PB1.pb1_m.getName() 則會出錯,因為 PA.PB1 中并沒有 pb1_m 。當(dāng)前 Local 中還是只有 PA 名字,并沒有 PA.PB1 名 字。
3) 當(dāng)執(zhí)行 #3 后,會將 PA.PB1 下的 pb1_m 載入內(nèi)存,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m 四個模塊,此時可以執(zhí)行 PA.PB1.pb1_m.getName() 了。由于使用了 as,當(dāng)前 Local中除了 PA 名字,另外添加了 m1 作為 PA.PB1.pb1_m 的別名。
4) 當(dāng)執(zhí)行 #4 后,會將 PA.PB2、PA.PB2.pb2_m 載入內(nèi)存,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m、PA.PB2、PA.PB2.pb2_m 六個模塊。當(dāng)前 Local 中還是只有 PA、m1。
下面的 #5,#6,#7 都是可以正確運行的。
注意的是:如果 PA.PB2.pb2_m 想導(dǎo)入 PA.PB1.pb1_m、PA.wave 是可以直接成功的。最好是采用明確的導(dǎo)入路徑,對于 ./.. 相對導(dǎo)入路徑還是不推薦用。
相關(guān)文章
Python基于sklearn庫的分類算法簡單應(yīng)用示例
這篇文章主要介紹了Python基于sklearn庫的分類算法,結(jié)合簡單實例形式分析了Python使用sklearn庫封裝樸素貝葉斯、K近鄰、邏輯回歸、SVM向量機(jī)等常見機(jī)器學(xué)習(xí)算法的分類調(diào)用相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
python實現(xiàn)學(xué)生管理系統(tǒng)源碼
這篇文章主要為大家詳細(xì)介紹了python實現(xiàn)學(xué)生管理系統(tǒng)源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-04-04
在Python中執(zhí)行系統(tǒng)命令的方法示例詳解
最近在做那個測試框架的時候發(fā)現(xiàn)對python執(zhí)行系統(tǒng)命令不太熟悉,所以想著總結(jié)下,下面這篇文章主要給大家介紹了關(guān)于在Python中執(zhí)行系統(tǒng)命令的方法,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09

