module是一個Python檔案,而package是一個目錄結構。package中必須包含__init__.py, 該檔案可以為空白,主要作用是讓python識別這個目錄為package。
__init__.py中可以使用__all__這個列表來指定在from this_module import *時時,哪些模組會被自動載入
__all__ = [ 'test' ] module的字串名
__init__.py會在import該package或者該package中的Module之前被執行,並且只執行一次。
當import package時,將會只執行該__init__.py,我們可以指定需要載入的子module。這樣在載入該包後,這些module就可用了。
當from package import *時,__all__定義的是需要匯入的模組
當from module import *時,__all__定義的是匯入的名稱
Module Hierachy
我們知道os是一個module,而path是os的sub module,他們不是以package的形式來組織的。如果我們要以module,submodule的形式來組織層級結構,比如os.path,那麼我們該怎麼做?
當我們import os.path時,其實python是這麼來載入的這些module的
importing_process
1. search sys.modules
2. find a finder
call finder.find_modules(fullname), if found, return a loader
3. call loader.load_modules(fullname)
4. add to sys.modules
importing os (fullname = os)
importing path (fullname = os.path)
因為module name中出現了dot,所以python會對每個identifier進行分別處理,他會現在sys.modules中找os,如果找到,ok,繼續import path。在準備import path時,實際使用的是os.path這個fullname,而不是簡單的identifier。假設os.path還沒有載入(在sys.modules中沒有找到),那麼他會尋找能夠找到path的finder,如果這個finder能夠找到os.path,那麼返回一個loader,該loader會被調用,來載入module,如果失敗,則拋出ImportError異常。
由於os、path都是module,所以如果不做特殊處理,Python會報
Traceback (most recent call last): File "", line 1, in <module>ImportError: No module named path
查看os中的代碼可以知道,os在import path後,將其添加到sys.modules中,
# os.pyimport path # persudo codesys.modules['os.path'] = path
這樣,當第二次python試圖匯入path時,將會直接在sys.modules中找到os.path,從而避免了ImportError。
我們要做的就是,在匯入第一個parent module時,手動匯入submodule,並且將submodule添加到sys.modules中。submodule必須由parent module預先添加到sys.modules中,一方面是為了防止ImportError,另一方面是為了submodule還能夠被部分獨立地重用。
考慮我們有3個module,分別是a, b, c,前一個是後一個Parent。所以可以這麼實現,
# a.pyimport bimport syssys.modules['a.b'] = bsys.modules['a.b.c'] = b.c# b.pyimport cimport syssys.modules['b.c'] = c
優點:簡單,不用以目錄形式組織module hierachy
缺點:複雜,需要手動將submodule加入到sys.modules
Package HierachyTODO:
Reference: [1] Python 2.7 documentation - keyword sys.modules
[2] New Import Hooks - Specification part 1: The Importer Protocol