python中的import(涉及pkgutil和inspect包)


python中萬物皆對象。維度比較大的有模塊、包。

一個.py文件就是一個python模塊(module),如果一個目錄下面有一個__init__.py文件,那么這個目錄就是一個python包(package)。

當然,這只是極簡版的概念。實際上包是一種特殊的模塊,而任何定義了__path__
屬性的模塊都被當做包。

以兩個下劃線開頭,以兩個下劃線結尾的屬性,暫稱魔法屬性(自創的),對應的有魔法方法。——用特別的格式表示能實現一些特別的功能。

在python代碼中使用模塊或包,需要使用import語句。

有兩種import方法:relative import和absolute import。

相對導入,是通過指出相對當前目錄位置的偏移來導入對應目錄下的模塊。

絕對導入,就是直接指出哪個包,哪個模塊。

當python解釋器看到import語句后,要做兩件事:找到module,將module加入到local namespace中。

具體就是,根據import的指示,去尋找(find_module)到對應的module,并導入(load_module)了。

在第一步查找時,遵循:

  1. 檢查 sys.modules (保存了之前import的類庫的緩存),如果module被找到,則?到第二步。
  1. 檢查 sys.meta_pathmeta_path 是一個 list,?面保存著一些 finder 對象,如果找到該module的話,就會返回一個finder對象。
  2. 檢查?些隱式的finder對象,不同的python實現有不同的隱式finder,但是都會有 sys.path_hooks, sys.path_importer_cache 以及sys.path
  3. 拋出 ImportError

詳細內容可以參考:
Python Import 機制與拓展
Python 的 import 機制

這里說幾個典型應用吧。

判斷模塊是否已導入
若已導入,必定在sys.modules中。sys.modules是已導入模塊的字典,key是模塊名,value是模塊對象。

獲取某已導入模塊的所有屬性
dir(sys.modules[module_name])獲取對應key==module_namevalue值。
help(dir)的輸出:

Help on built-in function dir in module __builtin__:

dir(...)
    dir([object]) -> list of strings

    If called without an argument, return the names in the current scope.
    Else, return an alphabetized list of names comprising (some of) the attributes
    of the given object, and of attributes reachable from it.
    If the object supplies a method named __dir__, it will be used; otherwise
    the default dir() logic is used and returns:
      for a module object: the module's attributes.
      for a class object:  its attributes, and recursively the attributes
        of its bases.
      for any other object: its attributes, its class's attributes, and
        recursively the attributes of its class's base classes.

在其中提到了,可以使用__dir__魔法方法來自定義。

之后在inspect或是需要了解對象內部的情況時,還會用到它。

為什么會報ImportError,如何規定導入路徑
sys.path 是當前環境的module搜索路徑,如果想要找到某package,就需要將此package的目錄加入到這個list當中,也就是對應package中的__init__.py文件所在的目錄。

如使用pip安裝的包就位于'/Library/Python/2.7/site-packages'目錄。

通常,在這個列表中,會加入當前目錄。

一個關于Class A和Class B遞歸導入的經典問題
import 迷宮

sys.meta_path和其他path hook的區別
處理sys.path時會使用sys.path_hooks,它會順序地檢查sys.path中的每一項,如不能處理則拋出ImportError,如果可以返回一個importer對象,并返回。
sys.meta_path見下。

自定義導入,寫一個import hook
可以通過import hook,來在模塊導入時做一些工作。

我們已經知道,導入模塊首先是要進行查找。而查找的第一步是檢查sys.modules,第二步是檢查sys.meta_path,使用其中的finder對象來查找所需要的module。
正常情況下,sys.meta_path是一個空列表。

finder對象必須實現find_module()方法,它可以找到模塊,并返回一個load_module()方法。
loader對象負責加載模塊,必須實現load_module()方法。
importer對象,實現了find_module()load_module()方法,即在實際中,只需要實現一個importer類,此類有find_module()load_module()兩個方法。

具體可見上面引用的第一篇blog。

函數、類屬于哪個模塊
在一個文件中,可以通過from module_name import function_name或者from module_name import class_name,來導入其他模塊的函數,或類。

在這種情況下,會發現這些導入的對象,也存在于該模塊的dir輸出中。

可以通過這些對象的__module__魔法屬性來判斷是在本模塊中定義,還是從外部導入。

使用這個方法,也可以得知某一方法是與類屬于同一模塊,還是繼承自基類(我并沒有去驗證基類和子類屬于同一模塊的情況,這種情況應該比較罕見吧?)。

相對導入和絕對導入

絕對導入就是明確指出是從哪個包或模塊導入。

相對導入是指相對于目前模塊所處的文件位置,來找到某個模塊來導入。如from . import xxx是從當前模塊所在的文件的同層目錄中尋找,一個點表示同層目錄,兩個點表示上一層目錄,以此類推。

相對導入會有一些麻煩。推薦使用絕對導入,這也是python推薦的。通過設置sys.path來將路徑加入,這樣在import時,python會到設置的路徑中自動匹配。

pkgutil

最常用的有兩個:

iter_modules(path=None, prefix='')
Yields (module_loader, name, ispkg) for all submodules on path, or, if path is None, all top-level modules on sys.path.
path是包的目錄路徑,prefix是輸出時,所有包的名字的前綴。用來獲取該path下的子模塊或子包。

walk_packages(path=None, prefix='', onerror=None)
Yields (module_loader, name, ispkg) for all modules recursively on path, or, if path is None, all accessible modules.
同上,但是這個方法是遞歸獲取路徑下的所有模塊。

inspect

常同pkgutil結合用。

getmembers(object, predicate=None)
返回object的所有屬性,即dir中的各項。
這些屬性各種各樣,通過設置predicate來進行篩選,如當predicate=ismethod,只有當其為類的方法時,才會被選中。

getdoc(object)
獲取對象的docstring,即__doc__

getargspec(func)
獲取函數對象的arg。

還有很多其他方法,詳見inspect的幫助文檔。


最后的TODO

最近在做一個自動化測試的項目,第一步就是要找到所有的模塊、類、方法、函數,以上就是涉及到的一部分基礎知識。

過程中,發現dir輸出的內容要遠遠多于真實在代碼里寫出來的東西,對于方法或屬性,也需要去了解一下。

Python 魔術方法指南可以起到一定的幫助作用,剩下的還需要繼續搜索。


其他相關博文:
python非侵入式代碼監控(一): python import hook
PEP 302 -- New Import Hooks
PEP 369 -- Post import hooks
Python源碼剖析筆記5-模塊機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • 模塊和包 一 模塊 1 什么是模塊? 常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是...
    go以恒閱讀 2,289評論 0 4
  • 當前目錄 和 腳本目錄 參考資料:https://techibee.com/python/get-current-...
    ThomasYoungK閱讀 11,082評論 0 11
  • If you quit from the Python interpreter and enter it agai...
    linyk3閱讀 374評論 0 0
  • IO密集型程序、深拷貝和淺拷貝、模塊導入、with 語句 1.1 GIL 學習目標 1. 能夠說出 GIL 是什...
    Cestine閱讀 838評論 0 0
  • [TOC] 最開始寫程序的時候,都是一個文件里輸入幾行源碼(python 的一個 web 框架bottle就特別強...
    人世間閱讀 5,537評論 1 10