1. 標(biāo)準(zhǔn) import
Python 中所有加載到內(nèi)存的模塊都放在 sys.modules 断凶。當(dāng) import 一個(gè)模塊時(shí)首先會(huì)在這個(gè)列表中查找是否已經(jīng)加載了此模塊讹蘑,如果加載了則只是將模塊的名字加入到正在調(diào)用 import 的模塊的 Local 名字空間中鼠渺。如果沒有加載則從 sys.path 目錄中按照模塊名稱查找模塊文件提前,模塊可以是py崇渗、pyc、pyd急前,找到后將模塊載入內(nèi)存醒陆,并加到 sys.modules 中,并將名稱導(dǎo)入到當(dāng)前的 Local 名字空間裆针。
一個(gè)模塊不會(huì)重復(fù)載入刨摩。多個(gè)不同的模塊都可以用 import 引入同一個(gè)模塊到自己的 Local 名字空間,其實(shí)背后的 PyModuleObject 對(duì)象只有一個(gè)据块。說一個(gè)容易忽略的問題:import 只能導(dǎo)入模塊码邻,不能導(dǎo)入模塊中的對(duì)象(類、函數(shù)另假、變量等)。例如:模塊 A(A.py)中有個(gè)函數(shù) getName怕犁,另一個(gè)模塊不能通過 import A.getName 將 getName導(dǎo)入到本模塊边篮,只能用 fromA import getName。
2. 嵌套 import
1)順序嵌套
例如:本模塊導(dǎo)入 A 模塊(import A)奏甫,A 中又 import B戈轿,B 模塊又可以 import 其他模塊……
這中嵌套比較容易理解,需要注意的一點(diǎn)就是各個(gè)模塊的 Local 名字空間是獨(dú)立的阵子。對(duì)于上面的例子思杯,本模塊 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 的時(shí)候不能加載 D 呢领突?
如果將 A.py 改為:import B 就可以了暖璧。
這是怎么回事呢?
RobertChen:這跟Python內(nèi)部 import 的機(jī)制是有關(guān)的君旦,具體到 from B import D澎办,Python 內(nèi)部會(huì)分成幾個(gè)步驟:
(1)在 sys.modules 中查找符號(hào) “B”
(2)如果符號(hào) B 存在,則獲得符號(hào) B 對(duì)應(yīng)的 module 對(duì)象金砍。
從 的 __dict__ 中獲得符號(hào) “D” 對(duì)應(yīng)的對(duì)象局蚀,如果 “D” 不存在,則拋出異常恕稠。
(3)如果符號(hào) B 不存在琅绅,則創(chuàng)建一個(gè)新的 module 對(duì)象 ,注意谱俭,此時(shí)奉件,module 對(duì)象的 __dict__ 為空宵蛀。
執(zhí)行 B.py 中的表達(dá)式,填充的 __dict__县貌。
從的 __dict__ 中獲得 “D” 對(duì)應(yīng)的對(duì)象术陶,如果 “D” 不存在,則拋出異常煤痕。
所以這個(gè)例子的執(zhí)行順序如下:
1梧宫、執(zhí)行 A.py 中的 from B import D 由于是執(zhí)行的 python A.py,所以在 sys.modules 中并沒有 存在摆碉, 首先為 B.py 創(chuàng)建一個(gè) module 對(duì)象 () 塘匣, 注意,這時(shí)創(chuàng)建的這個(gè) module 對(duì)象是空的巷帝,里邊啥也沒有忌卤, 在 Python 內(nèi)部創(chuàng)建了這個(gè) module 對(duì)象之后,就會(huì)解析執(zhí)行 B.py楞泼,其目的是填充 這個(gè) __dict__驰徊。
2、執(zhí)行 B.py中的from A import C 在執(zhí)行B.py的過程中堕阔,會(huì)碰到這一句棍厂, 首先檢查sys.modules這個(gè)module緩存中是否已經(jīng)存在了, 由于這時(shí)緩存還沒有緩存超陆, 所以類似的牺弹,Python內(nèi)部會(huì)為A.py創(chuàng)建一個(gè)module對(duì)象(), 然后时呀,同樣地张漂,執(zhí)行A.py中的語句
3、再次執(zhí)行A.py中的from B import D 這時(shí)退唠,由于在第1步時(shí)鹃锈,創(chuàng)建的對(duì)象已經(jīng)緩存在了sys.modules中, 所以直接就得到了瞧预, 但是屎债,注意,從整個(gè)過程來看垢油,我們知道盆驹,這時(shí)還是一個(gè)空的對(duì)象,里面啥也沒有滩愁, 所以從這個(gè)module中獲得符號(hào)"D"的操作就會(huì)拋出異常躯喇。 如果這里只是import B,由于"B"這個(gè)符號(hào)在sys.modules中已經(jīng)存在,所以是不會(huì)拋出異常的廉丽。
ZQ:圖解
啄木鳥社區(qū)《import 迷宮》:http://wiki.woodpecker.org.cn/moin/MiscItems/2008-11-25
3. 包 import
只要一個(gè)文件夾下面有個(gè) __init__.py 文件倦微,那么這個(gè)文件夾就可以看做是一個(gè)包。包導(dǎo)入的過程和模塊的基本一致正压,只是導(dǎo)入包的時(shí)候會(huì)執(zhí)行此包目錄下的__init__.py而不是模塊里面的語句了欣福。另外,如果只是單純的導(dǎo)入包焦履,而包的 __init__.py 中又沒有明確的其他初始化操作拓劝,那么此包下面的模塊是不會(huì)自動(dòng)導(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 會(huì)同時(shí)存在 PA郑临、PA.wave 兩個(gè)模塊,此時(shí)可以調(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 中會(huì)有 PA、 PA.wave秋柄、PA.PB1 三個(gè)模塊,但是 PA.PB1 下的任何模塊都沒有自動(dòng)載入內(nèi)存蠢正,此時(shí)如果直接執(zhí)行 PA.PB1.pb1_m.getName() 則會(huì)出錯(cuò)骇笔,因?yàn)?PA.PB1 中并沒有 pb1_m 。當(dāng)前 Local 中還是只有 PA 名字嚣崭,并沒有 PA.PB1 名 字笨触。
3) 當(dāng)執(zhí)行 #3 后,會(huì)將 PA.PB1 下的 pb1_m 載入內(nèi)存雹舀,sys.modules 中會(huì)有 PA芦劣、PA.wave、PA.PB1说榆、PA.PB1.pb1_m 四個(gè)模塊虚吟,此時(shí)可以執(zhí)行 PA.PB1.pb1_m.getName() 了。由于使用了 as签财,當(dāng)前 Local中除了 PA 名字串慰,另外添加了 m1 作為 PA.PB1.pb1_m 的別名。
4) 當(dāng)執(zhí)行 #4 后唱蒸,會(huì)將 PA.PB2邦鲫、PA.PB2.pb2_m 載入內(nèi)存,sys.modules 中會(huì)有 PA神汹、PA.wave庆捺、PA.PB1古今、PA.PB1.pb1_m、PA.PB2滔以、PA.PB2.pb2_m 六個(gè)模塊捉腥。當(dāng)前 Local 中還是只有 PA、m1醉者。
下面的 #5但狭,#6,#7 都是可以正確運(yùn)行的撬即。
注意的是:如果 PA.PB2.pb2_m 想導(dǎo)入 PA.PB1.pb1_m立磁、PA.wave 是可以直接成功的。最好是采用明確的導(dǎo)入路徑剥槐,對(duì)于 ./.. 相對(duì)導(dǎo)入路徑還是不推薦用唱歧。