1.1什么是import機制?
通常來講,在一段Python代碼中去執行引用另一個模塊中的代碼,就需要使用Python的import機制。import語句是觸發import機制最常用的手段,但并不是唯一手段。
importlib.import_module和__import__函數也可以用來引入其他模塊的代碼。
1.2import是如何執行的?
import語句會執行兩步操作:
搜索需要引入的模塊
將模塊的名字做為變量綁定到局部變量中
搜索步驟實際上是通過__import__函數完成的,而其返回值則會作為變量被綁定到局部變量中。下面我們會詳細聊到__import__函數是如果運作的。
二、import機制概覽
下圖是import機制的概覽圖。不難看出,當import機制被觸發時,Python首先會去sys.modules中查找該模塊是否已經被引入過,如果該模塊已經被引入了,就直接調用它,否則再進行下一步。這里sys.modules可以看做是一個緩存容器。值得注意的是,如果sys.modules中對應的值是None那么就會拋出一個ModuleNotFoundError異常。下面是一個簡單的實驗:
In[1]:importsys
In[2]:sys.modules['os']=None
In[3]:importos
---------------------------------------------------------------------------
ModuleNotFoundErrorTraceback(mostrecentcalllast)
in
---->1importos
ModuleNotFoundError:importofoshalted;Noneinsys.modules
如果在sys.modules找到了對應的module,并且這個import是由import語句觸發的,那么下一步將對把對應的變量綁定到局部變量中。
如果沒有發現任何緩存,那么系統將進行一個全新的import過程。在這個過程中Python將遍歷sys.meta_path來尋找是否有符合條件的元路徑查找器(metapathfinder)。sys.meta_path是一個存放元路徑查找器的列表。它有三個默認的查找器:
內置模塊查找器
凍結模塊(frozenmodule)查找器
基于路徑的模塊查找器。
In[1]:importsys
In[2]:sys.meta_path
Out[2]:
[_frozen_importlib.BuiltinImporter,
_frozen_importlib.FrozenImporter,
_frozen_importlib_external.PathFinder]
查找器的find_spec方法決定了該查找器是否能處理要引入的模塊并返回一個ModeuleSpec對象,這個對象包含了用來加載這個模塊的相關信息。如果沒有合適的ModuleSpec對象返回,那么系統將查看sys.meta_path的下一個元路徑查找器。如果遍歷sys.meta_path都沒有找到合適的元路徑查找器,將拋出ModuleNotFoundError。引入一個不存在的模塊就會發生這種情況,因為sys.meta_path中所有的查找器都無法處理這種情況:
In[1]:importnosuchmodule
---------------------------------------------------------------------------
ModuleNotFoundErrorTraceback(mostrecentcalllast)
in
---->1importnosuchmodule
ModuleNotFoundError:Nomodulenamed'nosuchmodule'
但是,如果這個手動添加一個可以處理這個模塊的查找器,那么它也是可以被引入的:
In[1]:importsys
...:
...:fromimportlib.abcimportMetaPathFinder
...:fromimportlib.machineryimportModuleSpec
...:
...:classNoSuchModuleFinder(MetaPathFinder):
...:deffind_spec(self,fullname,path,target=None):
...:returnModuleSpec('nosuchmodule',None)
...:
...:#don'tdothisinyourscript
...:sys.meta_path=[NoSuchModuleFinder()]
...:
...:importnosuchmodule
---------------------------------------------------------------------------
ImportErrorTraceback(mostrecentcalllast)
in
11sys.meta_path=[NoSuchModuleFinder()]
12
--->13importnosuchmodule
ImportError:missingloader
可以看到,當我們告訴系統如何去find_spec的時候,是不會拋出ModuleNotFound異常的。但是要成功加載一個模塊,還需要加載器loader。
加載器是ModuleSpec對象的一個屬性,它決定了如何加載和執行一個模塊。如果說ModuleSpec對象是“師父領進門”的話,那么加載器就是“修行在個人”了。在加載器中,你完全可以決定如何來加載以及執行一個模塊。這里的決定,不僅僅是加載和執行模塊本身,你甚至可以修改一個模塊:
In[1]:importsys
...:fromtypesimportModuleType
...:fromimportlib.machineryimportModuleSpec
...:fromimportlib.abcimportMetaPathFinder,Loader
...:
...:classModule(ModuleType):
...:def__init__(self,name):
...:self.x=1
...:self.name=name
...:
...:classExampleLoader(Loader):
...:defcreate_module(self,spec):
...:returnModule(spec.name)
...:
...:defexec_module(self,module):
...:module.y=2
...:
...:classExampleFinder(MetaPathFinder):
...:deffind_spec(self,fullname,path,target=None):
...:returnModuleSpec('module',ExampleLoader())
...:
...:sys.meta_path=[ExampleFinder()]
In[2]:importmodule
In[3]:module
Out[3]:)>
In[4]:module.x
Out[4]:1
In[5]:module.y
Out[5]:2
從上面的例子可以看到,一個加載器通常有兩個重要的方法create_module和exec_module需要實現。如果實現了exec_module方法,那么create_module則是必須的。如果這個import機制是由import語句發起的,那么create_module方法返回的模塊對象對應的變量將會被綁定到當前的局部變量中。如果一個模塊因此成功被加載了,那么它將被緩存到sys.modules。如果這個模塊再次被加載,那么sys.modules的緩存將會被直接引用。
以上內容為大家介紹了Python的import機制,希望對大家有所幫助,如果想要了解更多Python相關知識,請關注IT培訓機構:千鋒教育。