python的學習(十七)—-import機制

來源:互聯網
上載者:User

發現目前代碼越多,import就顯得越雜亂,只能加強下模組匯入的管理。

模組的搜尋路徑

模組的搜尋路徑都放在了sys.path列表中,如果預設的sys.path中沒有含有自己的模組或包的路徑,可以動態加入(sys.path.apend)即可。下面是sys.path在Windows平台下的添加規則。

1、sys.path第一個路徑往往是主模組所在的目錄。在互動環境下添加一個空項,它對應目前的目錄。

2、如果PYTHONPATH環境變數存在,sys.path會載入此變數指定的目錄。

3、我們嘗試找到Python Home,如果設定了PYTHONHOME環境變數,我們認為這就是Python Home,否則,我們使用python.exe所在目錄找到lib/os.py去推斷Python Home。如果我們確實找到了Python Home,則相關的子目錄(Lib、plat-win、lib-tk等)將以Python Home為基礎加入到sys.path,並匯入(執行)lib/site.py,將site-specific目錄及其下的包加入。如果我們沒有找到Python
Home,則把註冊表Software/Python/PythonCore/2.5/PythonPath的項加入sys.path(HKLM和 HKCU合并後加入),但相關的子目錄不會自動添加的。

4、如果我們沒有找到Python Home,並且沒有PYTHONPATH環境變數,並且不能在註冊表中找到PythonPath,那麼預設相對路徑將加入(如:./Lib;./plat-win等)。

總結如下

當在安裝好的主目錄中運行Python.exe時,首先推斷Python Home,如果找到了PythonHome,註冊表中的PythonPath將被忽略;否則將註冊表的PythonPath加入。

如果PYTHONPATH環境變數存在,sys.path肯定會載入此變數指定的目錄。

如果Python.exe在另外的一個目錄下(不同的目錄,比如通過COM嵌入到其他程式),Python Home將不推斷,此時註冊表的PythonPath將被使用。

如果Python.exe不能發現他的主目錄(PythonHome),並且註冊表也沒有PythonPath,則將加入預設的相對目錄。

標準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匯入到本模組,只能用import A。如果想只匯入特定的類、函數、變數則用from A import getName即可。

嵌套Import

嵌套import,我分兩種情況,一種是:本模組匯入A模組(import A),而A中又有import語句,會啟用另一個import動作,如import B,而B模組又可以import其他模組,一直下去。對這種嵌套比較容易理解,注意一點就是各個模組的Local名字空間是獨立的,所以上面的例子,本模組import A完了後本模組只能訪問模組A,不能訪問B及其他模組。雖然模組B已經載入到記憶體了,如果要訪問還要在明確的在本模組中import B。

另外一種嵌套指,在模組A中import B,而在模組B中import A。這時會怎麼樣呢?這個在Python列表中由RobertChen給出了詳細解釋,抄錄如下:

[A.py]  from B import D  class C:pass  [B.py]  from A import C  class D:pass

為什麼執行A的時候不能載入D呢?

如果將A.py改為:import B就可以了。

這是怎麼回事呢?

RobertChen:這跟Python內部import的機制是有關的,具體到from B import D,Python內部會分成幾個步驟:

  1. 在sys.modules中尋找符號"B"
  2. 果符號B存在,則獲得符號B對應的module對象<module B>。

    從<module 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中並沒有<moduleB>存在,首先為B.py建立一個module對象(<moduleB>),注意,這時建立的這個module對象是空的,裡邊啥也沒有,在Python內部建立了這個module對象之後,就會解析執行B.py,其目的是填充<module B>這個dict。

2、執行B.py中的from A import C

在執行B.py的過程中,會碰到這一句,首先檢查sys.modules這個module緩衝中是否已經存在<moduleA>了,由於這時緩衝還沒有緩衝<moduleA>,所以類似的,Python內部會為A.py建立一個module對象(<moduleA>),然後,同樣地,執行A.py中的語句。

3、再次執行A.py中的from B import D

這時,由於在第1步時,建立的<moduleB>對象已經緩衝在了sys.modules中,所以直接就得到了<moduleB>,但是,注意,從整個過程來看,我們知道,這時<moduleB>還是一個空的對象,裡面啥也沒有,所以從這個module中獲得符號"D"的操作就會拋出異常。如果這裡只是importB,由於"B"這個符號在sys.modules中已經存在,所以是不會拋出異常的。

上面的解釋已經由Zoom.Quiet收錄在啄木鳥了,裡面有圖,可以參考一下。

Package(包) Import

包(Package)可以看成模組的集合,只要一個檔案夾下面有個__init__.py檔案,那麼這個檔案夾就可以看做是一個包。包下面的檔案夾還可以成為包(子包)。更進一步,多個較小的包可以彙總成一個較大的包,通過包這種結構,方便了類的管理和維護,也方便了使用者的使用。比如SQLAlchemy等都是以包的形式發布給使用者的。包和模組其實是很類似的東西,如果查看包的類型import SQLAlchemy type(SQLAlchemy),可以看到其實也是<type
'module'>。import包的時候尋找的路徑也是sys.path。包匯入的過程和模組的基本一致,只是匯入包的時候會執行此包目錄下的__init__.py而不是模組裡面的語句了。另外,如果只是單純的匯入包,而包的__init__.py中又沒有明確的其他初始化操作,那麼此包下面的模組是不會自動匯入的。如:

PA

--__init__.py

--wave.py

--PB1

  --__init__.py

  --pb1_m.py

--PB2

  --__init__.py

  --pb2_m.py

__init__.py都為空白,如果有以下程式:

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

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

當執行#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後,會將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後,會將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是可以直接成功的。最好是採用明確的匯入路徑,對於./..相對匯入路徑還是不推薦用。

[轉自]http://blog.163.com/hzr163_2004/blog/static/3308607520092113930771/]

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.