python之import機制詳解_python

來源:互聯網
上載者:User

本文詳述了Python的import機制,對於理解Python的運行機制很有協助!

1.標準import:

Python中所有載入到記憶體的模組都放在 sys.modules 。當 import 一個模組時首先會在這個列表中尋找是否已經載入了此模組,如果載入了則只是將模組的名字加入到正在調用 import 的模組的 Local 名字空間中。如果沒有載入則從 sys.path 目錄中按照模組名稱尋找模組檔案,模組可以是py、pyc、pyd,找到後將模組載入記憶體,並加到 sys.modules 中,並將名稱匯入到當前的 Local 名字空間。

一個模組不會重複載入。多個不同的模組都可以用 import 引入同一個模組到自己的 Local 名字空間,其實背後的 PyModuleObject 對象只有一個。這裡說一個容易忽略的問題:import 只能匯入模組,不能匯入模組中的對象(類、函數、變數等)。例如:模組 A(A.py)中有個函數 getName,另一個模組不能通過 import A.getName 將 getName匯入到本模組,只能用 from A import getName。

2.嵌套import:

1)順序嵌套

例如:本模組匯入 A 模組(import A),A 中又 import B,B 模組又可以 import 其他模組……
這中嵌套比較容易理解,需要注意的一點就是各個模組的 Local 名字空間是獨立的。對於上面的例子,本模組 import A 之後本模組只能訪問模組 A,不能訪問模組 B 及其他模組。雖然模組 B 已經載入到記憶體了,如果訪問還要再明確的在本模組中 import B。

2)迴圈嵌套

例如:

檔案[A.py]

from B import Dclass C:pass

檔案[ B.py ]

from A import Cclass D:pass

為什麼執行 A 的時候不能載入 D 呢?
如果將 A.py 改為:import B 就可以了。
這是怎麼回事呢?

RobertChen:這跟Python內部 import 的機制是有關的,具體到 from B import D,Python 內部會分成幾個步驟:
(1)在 sys.modules 中尋找符號 “B”
(2)如果符號 B 存在,則獲得符號 B 對應的 module 對象。
  從 <modult B> 的 __dict__ 中獲得符號 “D” 對應的對象,如果 “D” 不存在,則拋出異常。
(3)如果符號 B 不存在,則建立一個新的 module 對象 <module B>,注意,此時,module 對象的 __dict__ 為空白。
執行 B.py 中的運算式,填充 <module B> 的 __dict__。
從  <module B> 的 __dict__ 中獲得 “D” 對應的對象,如果 “D” 不存在,則拋出異常。

所以這個例子的執行順序如下:

1、執行 A.py 中的 from B import D 由於是執行的 python A.py,所以在 sys.modules 中並沒有 <module B> 存在, 首先為 B.py 建立一個 module 對象 (<module B>) , 注意,這時建立的這個 module 對象是空的,裡邊啥也沒有, 在 Python 內部建立了這個 module 對象之後,就會解析執行 B.py,其目的是填充 <module B> 這個 __dict__。
2、執行 B.py中的from A import C 在執行B.py的過程中,會碰到這一句, 首先檢查sys.modules這個module緩衝中是否已經存在<module A>了, 由於這時緩衝還沒有緩衝<module A>, 所以類似的,Python內部會為A.py建立一個module對象(<module A>), 然後,同樣地,執行A.py中的語句
3、再次執行A.py中的from B import D 這時,由於在第1步時,建立的<module B>對象已經緩衝在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,從整個過程來看,我們知道,這時<module B>還是一個空的對象,裡面啥也沒有, 所以從這個module中獲得符號"D"的操作就會拋出異常。 如果這裡只是import B,由於"B"這個符號在sys.modules中已經存在,所以是不會拋出異常的。

ZQ:圖解如下:

3. 包 import

只要一個檔案夾下面有個 __init__.py 檔案,那麼這個檔案夾就可以看做是一個包。包匯入的過程和模組的基本一致,只是匯入包的時候會執行此包目錄下的 __init__.py 而不是模組裡面的語句了。另外,如果只是單純的匯入包,而包的 __init__.py 中又沒有明確的其他初始化操作,那麼此包下面的模組是不會自動匯入的。
 例如:
  有下面的包結構:
  PA
  |---- __init__.py
  |---- wave.py
  |---- PB1
        |---- __init__.py
        |---- pb1_m.py
  |---- PB2
        |---- __init__.py
        |---- pb2_m.py
有如下程式:

import sysimport PA.wave              #1import PA.PB1                #2import PA.PB1.pb1_m as m1    #3import PA.PB2.pb2_m          #4PA.wave.getName()           #5m1.getName()                #6PA.PB.pb2_m.getName()       #7

1) 當執行 #1 後,sys.modules 會同時存在 PA、PA.wave 兩個模組,此時可以調用 PA.wave 的任何類或函數了。但不能調用 PA.PB1(2) 下的任何模組。當前 Local 中有了 PA 名字。

2) 當執行 #2 後,只是將 PA.PB1 載入記憶體,sys.modules 中會有 PA、 PA.wave、PA.PB1 三個模組,但是 PA.PB1 下的任何模組都沒有自動載入記憶體,此時如果直接執行 PA.PB1.pb1_m.getName() 則會出錯,因為 PA.PB1 中並沒有 pb1_m 。當前 Local 中還是只有 PA 名字,並沒有 PA.PB1 名 字。

3) 當執行 #3 後,會將 PA.PB1 下的 pb1_m 載入記憶體,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m 四個模組,此時可以執行 PA.PB1.pb1_m.getName() 了。由於使用了 as,當前 Local中除了 PA 名字,另外添加了 m1 作為 PA.PB1.pb1_m 的別名。

4) 當執行 #4 後,會將 PA.PB2、PA.PB2.pb2_m 載入記憶體,sys.modules 中會有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m、PA.PB2、PA.PB2.pb2_m 六個模組。當前 Local 中還是只有 PA、m1。
下面的 #5,#6,#7 都是可以正確啟動並執行。

注意的是:如果 PA.PB2.pb2_m 想匯入 PA.PB1.pb1_m、PA.wave 是可以直接成功的。最好是採用明確的匯入路徑,對於 ./.. 相對匯入路徑還是不推薦用。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.