python的import是在程序運(yùn)行期間執(zhí)行的,并非像其它很多語(yǔ)言一樣是在編譯期間執(zhí)行。也就是說(shuō),import可以出現(xiàn)在任何地方,只有執(zhí)行到這個(gè)import行時(shí),才會(huì)執(zhí)行導(dǎo)入操作。且在import某個(gè)模塊之前,無(wú)法訪問(wèn)這個(gè)模塊的屬性。
python在import導(dǎo)入模塊時(shí),首先搜索模塊的路徑,然后編譯并執(zhí)行這個(gè)模塊文件。雖然概括起來(lái)只有兩個(gè)過(guò)程,但實(shí)際上很復(fù)雜。
前文已經(jīng)解釋了import的模塊搜索過(guò)程,所以這里大概介紹import的其它細(xì)節(jié)。
以前面的a.py中導(dǎo)入模塊文件b.py為例:
importb
import導(dǎo)入模塊時(shí),搜索到模塊文件b.py后:
1.首先在內(nèi)存中為每個(gè)待導(dǎo)入的模塊構(gòu)建module類的實(shí)例:模塊對(duì)象。這個(gè)模塊對(duì)象目前是空對(duì)象,這個(gè)對(duì)象的名稱為全局變量b。
注意細(xì)節(jié):module類的對(duì)象,變量b。
輸出下它們就知道:
print(b)
print(type(b))
輸出結(jié)果:
因?yàn)閎是全局變量,所以當(dāng)前程序文件a.py中不能重新對(duì)全局變量b進(jìn)行賦值,這會(huì)使導(dǎo)入的模塊b被丟棄。例如,下面是錯(cuò)誤的:
importb
b=3
print(b.x)#已經(jīng)沒(méi)有模塊b了
另外,因?yàn)閕mport導(dǎo)入時(shí)是將模塊對(duì)象賦值給模塊變量,所以模塊變量名不能是python中的一些關(guān)鍵字,比如if、for等,這時(shí)會(huì)報(bào)錯(cuò)。雖然模塊文件名可以為list、keys等這樣的內(nèi)置函數(shù)名,但這會(huì)導(dǎo)致這些內(nèi)置函數(shù)不可用,因?yàn)楦鶕?jù)變量查找的作用域規(guī)則,首先查找全局變量,再查找內(nèi)置作用域。也就是說(shuō),模塊文件的文件名不能是這些關(guān)鍵字、也不應(yīng)該是這些內(nèi)置函數(shù)名。
File"g:/pycode/new.py",line11
importif
^
SyntaxError:invalidsyntax
2.構(gòu)造空模塊實(shí)例后,將編譯、執(zhí)行模塊文件b.py,并按照一定的規(guī)則將一些結(jié)果放進(jìn)這個(gè)模塊對(duì)象中。
注意細(xì)節(jié),編譯、執(zhí)行b.py、將結(jié)果保存到模塊對(duì)象中。
模塊第一次被導(dǎo)入的時(shí)候,會(huì)進(jìn)行編譯,并生成.pyc字節(jié)碼文件,然后python執(zhí)行這個(gè)pyc文件。當(dāng)模塊被再次導(dǎo)入時(shí),如果檢查到pyc文件的存在,且和源代碼文件的上一次修改時(shí)間戳mtime完全對(duì)應(yīng)(也就是說(shuō),編譯后源代碼沒(méi)有進(jìn)行過(guò)修改),則直接裝載這個(gè)pyc文件并執(zhí)行,不會(huì)再進(jìn)行額外的編譯過(guò)程。當(dāng)然,如果修改過(guò)源代碼,將會(huì)重新編譯得到新的pyc文件。
注意,并非所有的py文件都會(huì)生成編譯得到的pyc文件,對(duì)于那些只執(zhí)行一次的程序文件,會(huì)將內(nèi)存中的編譯結(jié)果在執(zhí)行完成后直接丟棄(多數(shù)時(shí)候如此,但仍有例外,比如使用compileall模塊可以強(qiáng)制編譯成pyc文件),但模塊會(huì)將內(nèi)存中的編譯結(jié)果持久化到pyc文件中。另外,運(yùn)行字節(jié)碼pyc文件并不會(huì)比直接運(yùn)行py文件更快,執(zhí)行它也一樣是一行行地解釋、執(zhí)行,唯一快的地方在于導(dǎo)入裝載的時(shí)候無(wú)需重新編譯而已。
執(zhí)行模塊文件(已完成編譯)的時(shí)候,按照一般的執(zhí)行流程執(zhí)行:一行一行地、以代碼塊為單元執(zhí)行。一般地,模塊文件中只用來(lái)聲明變量、函數(shù)等屬性,以便提供給導(dǎo)入它的模塊使用,而不應(yīng)該有其他任何操作性的行為,比如print()操作不應(yīng)該出現(xiàn)在模塊文件中,但這并非強(qiáng)制。
總之,執(zhí)行完模塊文件后,這個(gè)模塊文件將有一個(gè)自己的全局名稱空間,在此模塊文件中定義的變量、函數(shù)等屬性,都會(huì)記錄在此名稱空間中。
最后,模塊的這些屬性都會(huì)保存到模塊對(duì)象中。由于這個(gè)模塊對(duì)象賦值給了模塊變量b,所以通過(guò)變量b可以訪問(wèn)到這個(gè)對(duì)象中的屬性(比如變量、函數(shù)等),也就是模塊文件內(nèi)定義的全局屬性。
只導(dǎo)入一次
假設(shè)a.py中導(dǎo)入了模塊b和模塊sys,在b.py中也導(dǎo)入了模塊sys,但python默認(rèn)對(duì)某個(gè)模塊只會(huì)導(dǎo)入一次,如果a.py中先導(dǎo)入sys,再導(dǎo)入b,那么導(dǎo)入b并執(zhí)行b.py的時(shí)候,會(huì)發(fā)現(xiàn)sys已經(jīng)導(dǎo)入了,不會(huì)再去導(dǎo)入sys。
實(shí)際上,python執(zhí)行程序的時(shí)候,會(huì)將所有已經(jīng)導(dǎo)入的模塊放進(jìn)sys.module屬性中,這是一個(gè)dict,可以通過(guò)下面的方式查看已導(dǎo)入的模塊名:
>>>importsys
>>>list(sys.module.keys())
如果某個(gè)程序文件中多次使用import(或from)導(dǎo)入同一個(gè)模塊,雖然不會(huì)報(bào)錯(cuò),但實(shí)際上還是直接使用內(nèi)存中已裝載好的模塊對(duì)象。
例如,b.py中x=3,導(dǎo)入它之后修改該值,然后再次導(dǎo)入,發(fā)現(xiàn)b.x并不會(huì)發(fā)生改變:
importb
print(b.x)#3
b.x=33
print(b.x)#33
importb
print(b.x)#33
但是python提供了reload進(jìn)行多次重復(fù)導(dǎo)入的方法,見(jiàn)后文。
使用別名
import導(dǎo)入時(shí),可以使用as關(guān)鍵字指定一個(gè)別名作為模塊對(duì)象的變量,例如:
importbasbb
bb.x=3
print(bb.x)
這時(shí)候模塊對(duì)象將賦值給變量bb,而不是b,b此時(shí)不再是模塊對(duì)象變量,而僅僅只是模塊名。使用別名并不會(huì)影響性能,因?yàn)樗鼉H僅只是一個(gè)賦值過(guò)程,只不過(guò)是從原來(lái)的賦值對(duì)象變量b變?yōu)樽兞縝b而已。
以上內(nèi)容為大家介紹了Python導(dǎo)入模塊時(shí)的過(guò)程,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。