python之import機制


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 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 對象。
                從 <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:圖解
python之import機制
啄木鳥社區《import 迷宮》: http://wiki.woodpecker.org.cn/moin/MiscItems/2008-11-25

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 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.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 是可以直接成功的。最好是采用明確的導入路徑,對於 ./.. 相對導入路徑還是不推薦用。

原文:
http://blog.csdn.net/zbyufei/article/details/5894645
======================================
 

python中from module import * 的一個陷阱

 

from module import *把module中的成員全部導到了當前的global namespace,訪問起來就比較方便了。當然,python style一般不建議這么做,因為可能引起name conflict。

但還有另外一個問題 - 你以為你修改了某個變量,其實,被from module import *后的那個並沒有被更新,非常危險,因為程序有可能還可以正常運行, 只不過結果錯了,到了production才被發現就比較慘了。

舉個例子:

你定義了一些變量在base模塊中:

復制代碼
# reference data type
class Demo:
        def __init__(self, name):
                self.name = name
demo = Demo('Demo')

# primitive type
foo = 1
復制代碼

 

然后在一個模塊中用from  module import 的方式讀它:

復制代碼
from base import *
def read():
        print 'reference data id: ' + str(id(demo))
        print 'reference data value : ' + demo.name

        print 'primitive data id: ' + str(id(foo))
        print 'primitive data value: ' + str(foo)
復制代碼

在另外一個模塊中寫它:

復制代碼
import base

def write():
        print "\nOriginal:"
        print "Original reference data id: " + str(id(base.demo))
        base.demo.name = "Updated Demo" # this will reflect that change
        #base.demo = base.Demo("Updated Demo") # this won't relfect the change
        print "Original data id: " + str(id(base.foo))
        base.foo = 1000
        print "Original data id after assignment: " + str(id(base.foo))
復制代碼

 

然后先寫,后讀,看寫的內容是否有效:

復制代碼
import read
import write

print "before write"
read.read()

write.write()

print "\nafter write"
read.read()
復制代碼

 

結論是沒有,原因是:

  • 當你用from module import時,其實是copy了一份reference或者pointer,指向一份內存,var和module.var都指向同一份內存
  • 當你修改module.var時,其實你是讓它指向了另外一份內存,此時var和module.var指向的是不同的內存
  • 所以,雖然module.var的值變了,var還是指向原來那份內存,原來的值

這個對於object,比較容易理解,你可以直接修改object里的值,這個是有效的,但是當你指向另外一個object時就無效了。 對於primitive類型來講,其實也是一個道理,因為每次賦值,都是讓其指向一個不同的內存地址,而不是inplace修改已有的那份內存 -  這個很容易驗證:

復制代碼
In [1]: a = 10

In [2]: id(a)
Out[2]: 20429204

In [3]: a = 100

In [4]: id(a)
Out[4]: 20430108
復制代碼

 

所以,建議是除非是一個quick and dirty的腳本,否則不要使用from module import *!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM