2012-11-01 175 views
10

爲了序言,我想我可能已經想出瞭如何讓代碼工作(基於Changing module variables after import),但我的問題是關於爲什麼發生以下行爲,所以我可以理解什麼今後不做。導入模塊:__main__ vs導入爲模塊

我有三個文件。首先是mod1.py:

# mod1.py 

import mod2 

var1A = None 

def func1A(): 
    global var1 
    var1 = 'A' 
    mod2.func2() 

def func1B(): 
    global var1 
    print var1 

if __name__ == '__main__': 
    func1A() 

接下來我有mod2.py:

# mod2.py 

import mod1 

def func2(): 
    mod1.func1B() 

最後我有driver.py:

# driver.py 

import mod1 

if __name__ == '__main__': 
    mod1.func1A() 

如果我執行命令python mod1.py,則輸出是None。根據上面提到的鏈接,似乎mod1.py被導入爲__main__mod1.pymod2.py導入。因此,我創建了driver.py。如果我執行命令python driver.py然後我得到預期的輸出:A。我有點看出了區別,但我並沒有真正看到它的機制或原因。如何以及爲何發生這種情況?這似乎違反直覺,相同的模塊會存在兩次。如果我執行python mod1.py,是否可以訪問版本的__main__版本中的變量,而不是由mod2.py導入的版本中的變量?

+1

如果您重構以消除循環導入,您將爲自己做個忙。 – eryksun

回答

19

__name__變量總是包含模塊的名稱,,除了,此時文件已被作爲腳本加載到解釋器中。 然後改爲將該變量設置爲字符串'__main__'

畢竟,腳本是作爲整個程序的主文件運行的,其他所有內容都是由該主文件直接或間接導入的模塊。通過測試__name__變量,您可以檢測文件是作爲模塊導入還是直接運行。

在內部,模塊被賦予一個命名空間字典,該字典作爲每個模塊的元數據的一部分存儲在sys.modules中。主文件,即執行的腳本,存儲在與'__main__'相同的結構中。

但是,當您將文件作爲模塊導入時,python首先會在sys.modules中查找該模塊是否已經被導入。因此,import mod1意味着我們首先在sys.modules中查找mod1模塊。如果mod1還沒有,它將創建一個帶有名稱空間的新模塊結構。

所以,如果你都運行mod1.py爲主要文件,導入它作爲一個Python模塊,它會得到sys.modules 命名空間的條目。一個爲'__main__',後來爲'mod1'。這兩個名稱空間是完全分離的。您的全球var1存儲在sys.modules['__main__']中,但func1B正在查看sys.modules['mod1']var1,它是None

但是當你使用python driver.pydriver.py成爲該計劃的主要'__main__'文件,mod1將只是一次導入sys.modules['mod1']結構。這一次,func1A存儲var1sys.modules['mod1']結構,這就是func1B會發現。

+0

這樣好嗎? 'if __name__ =='__main__':sys.modules ['mod1'] = sys.modules ['__ main__']; func1A()' – Kos

+1

@Kos:我不會那麼做,我想你會發現很多假設都會被打破。相反,避免使用模塊作爲腳本。 –

+0

我一直在使用'if __name__ =='__main __':'爲模塊中的每個模塊編寫測試例程。有沒有繼續這種做法的好方法,或者我應該編寫測試程序作爲函數,然後在一個單獨的文件中有一個驅動程序,它除了導入模塊以進行測試並調用測試程序外什麼也不做。 – Brendan

1

關於用於使用模塊可選地作爲主腳本的實際解決方案 - 支撐一致的橫進口:

解決方案1:

參見例如在Python的PDB模塊,它是如何通過導入自己作爲__main__執行時(末)運行的腳本:

#! /usr/bin/env python 
"""A Python debugger.""" 
# (See pdb.doc for documentation.) 
import sys 
import linecache 

... 

# When invoked as main program, invoke the debugger on a script 
if __name__ == '__main__': 
    import pdb 
    pdb.main() 

只是我會建議__main__啓動重組,以這樣的腳本的開頭:

#! /usr/bin/env python 
"""A Python debugger.""" 
# When invoked as main program, invoke the debugger on a script 
import sys 
if __name__ == '__main__':   
    ##assert os.path.splitext(os.path.basename(__file__))[0] == 'pdb' 
    import pdb 
    pdb.main() 
    sys.exit(0) 

import linecache 
... 

這樣,模塊主體不會執行兩次 - 這是「代價高昂」,不可取,有時甚至是關鍵。

解決方案2:

在罕見的情況下,期望甚至直接暴露的實際腳本模塊__main__作爲實際模塊別名(mod1):

# mod1.py  
import mod2 

... 

if __name__ == '__main__': 
    # use main script directly as cross-importable module 
    _mod = sys.modules['mod1'] = sys.modules[__name__] 
    ##_modname = os.path.splitext(os.path.basename(os.path.realpath(__file__)))[0] 
    ##_mod = sys.modules[_modname] = sys.modules[__name__] 
    func1A() 

已知的缺點:

  • reload(_mod)失敗
  • pickle'ed類將需要額外的mappi ngs for unpickling(find_global ..)