2011-12-01 47 views
4

我想導入一個共享庫,其中包含一些可視化程序的Python包裝(具體來說就是VisIt)。這個庫的實現方式是首先導入一個庫,它提供了一些可用的函數,然後調用一個函數來啓動visualisaion查看器,並讓其餘的API可以調用。例如,在下面的導入一個共享庫和全局命名空間

form visit import * 
print dir() 
Launch() 
print dir() 

第一打印語句包含通常的內建和幾個其他功能

['AddArgument', 'GetDebugLevel', 'Launch', 'SetDebugLevel', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__'] 

和第二打印產量

['ActivateDatabase', 'AddArgument', 'AddColorTable', 'AddOperator', 'AddPlot', ... ] 

我想改爲在一個函數內調用Launch(所以我可以親將參數傳遞給Launch)。但是,當我這樣做時,在調用Launch後可用的函數不在全局名稱空間中,而是在函數本地的名稱空間中。因此,在下面的例子中

import sys 
from visit import * 

def main(): 

    Launch() 
    print dir() 
    if "Version" in dir() 
     print Version() # This is made available by call to Launch() above 

    return 0 

if __name__=="__main__": 
    ret = main() 
    print dir() 
    sys.exit(ret) 

mainprint語句將打印

['ActivateDatabase', 'AddArgument', 'AddColorTable', 'AddOperator', 'AddPlot', ... ] 

如上,main被稱爲打印

['AddArgument', 'GetDebugLevel', 'Launch', 'SetDebugLevel', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__'] 

而剛過print彷彿Launch從來沒有調用。

我的第一個問題是如何確保通過調用Launch來填充全局名稱空間?

其次,調用Version實際上失敗,並錯誤

NameError: global name 'Version' is not defined

即使print "Version" in dir()回報True。如果我解決了我的第一個問題,這個問題會得到解決嗎?還是完全是其他問題?

如果您需要共享庫上方的更多信息,請讓我知道。我不太瞭解它是如何寫的,但我可以試着弄清楚。

編輯:按照@voithos的回答,以下是我採用的解決方案。

正如@voithos所述,「訪問使用動態導入,將所有內容帶到本地範圍......假設您永遠不會在全球範圍外呼叫visit.Launch()」。他的(初始)答案允許我使用visit.Launch()提供的功能在我的主程序的外部(和內部)使用,使用前綴visit.以及所有這些例程。

要導入訪問程序如from visit import *,讓他們可以在沒有visit.前綴叫我修改@voithos'使用setattrmain以下

# Loop through the local namespace and add the names that were just 
# imported to the module namespace 
loc = locals() 
for key in loc: 
    setattr(sys.modules[__name__], key, loc[key]) 

然後訪問程序都可以在模塊級別,一切似乎都很好。

感謝@voithos的回答。

+0

我最初發布我的編輯作爲答案,但覺得將它作爲我的問題的更新會更好。 – Chris

回答

2

看來Visit使用了一個動態導入,它把所有東西帶到本地範圍。基本上,他們假設你永遠不會在全球範圍之外撥打visit.Launch()。啓動函數全部用C++編寫,所以我不確定它是如何導入的。

一種解決方法是將名稱從本地範圍重新分配到另一個範圍。 (例如,進入你導入visit模塊)下面是一個例子:

import sys 
import visit 

def main(): 
    visit.Launch() 

    # Loop through the locals that were just imported 
    # and assign the names to the visit module 
    loc = locals() 
    for key in loc: 
     setattr(visit, key, loc[key]) 

    return 0 

if __name__=="__main__": 
    ret = main() 
    print dir(visit) 
    sys.exit(ret) 

如果您想將名字分配給全球範圍內,那麼你可以修改環路:

loc = locals() 
glob = globals() 

for key in loc: 
    glob[key] = loc[key] 

但是請注意:這樣做會覆蓋您之前定義的任何衝突符號名稱。例如,如果您定義了Version()函數,則來自「訪問」模塊的Version()將覆蓋您定義的較舊函數。因此,除非您真的需要,否則不要將全局範圍與名稱混淆在一起通常是一個好主意。

+0

感謝您的回答@voithos - 這個效果很好,並且visit.Version,例如,現在可以調用'main'和'main'後成功調用。但是,我是否可以使用類似的解決方法將名爲local的名稱添加到全局名稱空間。如果我想在Visit中使用相同的腳本並在標準的Python解釋器中使用,這一點很重要。 – Chris

+0

@克里斯:當然,這很容易。只需分配給全局模塊,而不是「訪問」。你可以使用'globals()'來訪問全局符號表。我修改了答案來展示一個例子。 – voithos

0

我從來沒有用過的訪問,而是根據自己的文檔,你需要做的:

import visit 

def main():  
    visit.Launch() 

這將導入所有的功能整合到「訪問」的命名空間。然後,您只需通過訪問名稱空間與庫的函數進行交互。如:

visit.Version() 
+0

這給我帶來了同樣的問題:如果我使用'import visit'並在用'print dir(visit)'調用'main'後替換print語句'那麼我沒有得到完整的API,只有最初的函數調用'Launch'之前可用。 – Chris

+0

我應該說,爲什麼我使用'from visit import *'而不是'import visit'是有原因的。我的問題是'visit.Launch'提供的函數不能在'main'之外訪問。 – Chris