2010-02-27 35 views
4

我在Python中使用exec語句或execfile()運行腳本時查看了一些關於NameError異常的現有問題,但尚未找到有關以下行爲的良好解釋。爲什麼不導入在使用execfile()運行的python腳本中阻止NameError?

我想做一個簡單的遊戲,在運行時用execfile()創建腳本對象。下面是4個模塊來演示這個問題(請耐心等待,這很簡單,我可以做到!)。主程序只是用來加載使用的execfile()的腳本,然後調用腳本管理器來運行腳本對象:

# game.py 

import script_mgr 
import gamelib # must be imported here to prevent NameError, any place else has no effect 

def main(): 
    execfile("script.py") 
    script_mgr.run() 

main()

該腳本文件剛剛創建播放聲音的對象,然後將對象添加到列表中腳本管理器:

script.py 

import script_mgr 
#import gamelib # (has no effect here) 

class ScriptObject: 
    def action(self): 
    print("ScriptObject.action(): calling gamelib.play_sound()") 
    gamelib.play_sound() 

obj = ScriptObject() 
script_mgr.add_script_object(obj)

腳本管理器只是調用每個腳本的動作()函數:

# script_mgr.py 

#import gamelib # (has no effect here) 

script_objects = [] 

def add_script_object(obj): 
    script_objects.append(obj) 

def run(): 
    for obj in script_objects: 
    obj.action()

的gamelib函數的第四模塊中定義,這是被訪問的麻煩之一:

# gamelib.py 

def play_sound(): 
    print("boom!")

上面的代碼與下面的輸出工作:

 
mhack:exec $ python game.py 
ScriptObject.action(): calling gamelib.play_sound() 
boom! 
mhack:exec $ 

但是,如果我註釋掉的「進口gamelib」聲明game.py並取消在script.py「進口gamelib」,我得到以下錯誤:

mhack:exec $ python game.py 
ScriptObject.action(): calling gamelib.play_sound() 
Traceback (most recent call last): 
    File "game.py", line 10, in 
    main() 
    File "game.py", line 8, in main 
    script_mgr.run() 
    File "/Users/williamknight/proj/test/python/exec/script_mgr.py", line 12, in run 
    obj.action() 
    File "script.py", line 9, in action 
    gamelib.play_sound() 
NameError: global name 'gamelib' is not defined

我的問題是:1)爲什麼需要進口的「game.py」模塊中,該高層腳本中的一個? 2)爲什麼它不能從引用它的模塊(script.py)或它被調用的模塊(script_mgr.py)中導入'gamelib'?

這發生關於Python 2.5.1

回答

3

Python documentation爲的execfile:

的execfile(文件名[,全局[,當地人]])

如果省略當地人字典它默認爲globals字典。如果兩個字典均被省略,則在調用execfile()的環境中執行該表達式。

execfile有兩個可選參數。既然你省略了它們,你的腳本就會在execfile被調用的環境中執行。因此,在game.py中導入的原因會改變行爲。

另外,我得出的結論進口的game.py和script.py以下行爲:

  • 在game.py import gamelib進口gamelib模塊插入都全局和當地人。這是傳遞給script.py的環境,這就是爲什麼gamelib可以通過ScriptObject操作方法訪問(從全局訪問)的原因。

  • 在腳本中。py import gamelib導入gamelib模塊到只有當地人(不確定的原因)。所以當試圖從全局變量的ScriptObject操作方法訪問gamelib時,你有NameError。如果您將導入到動作方法的範圍如下:這將工作(gamelib將從當地人訪問):

    class ScriptObject: 
        def action(self): 
         import gamelib 
         print("ScriptObject.action(): calling gamelib.play_sound()") 
         gamelib.play_sound() 
    
+0

我知道全局和本地參數,但我仍然不確定如何最好地使用它們。你對環境的引用有助於我更好地理解,但是我仍然不明白爲什麼script.py中的導入不起作用 - 是不是也將它放入環境中? – 2010-02-28 00:05:50

+0

通過打印出全局變量和當地人在測試後更新了我的答案。希望它有幫助;) – Yukiko 2010-02-28 11:50:13

+0

是的,它確實有幫助!在您觀察到導入後對全局變量和本地變量的影響之後,我添加了一些調試代碼來打印出全局變量和本地變量,這真的讓事情變得清晰。我會接受你的答案,但也會對我的結果發佈後續答案。 – 2010-02-28 16:00:03

0

原因在script.py「導入gamelib」沒有效果是因爲它導入到game.py main()的本地範圍內,因爲這是執行導入的範圍。該範圍在執行時不是ScriptObject.action()的可見範圍。

添加調試代碼打印出來在全局()的變化和當地人()揭示了什麼是在程序中的以下修改版本回事:

# game.py 

import script_mgr 
import gamelib # puts gamelib into globals() of game.py 

# a debug global variable 
_game_global = "BEF main()" 

def report_dict(d): 
    s = "" 
    keys = d.keys() 
    keys.sort() 
    for i, k in enumerate(keys): 
    ln = "%04d %s: %s\n" % (i, k, d[k]) 
    s += ln 
    return s 

def main(): 
    print("--- game(): BEF exec: globals:\n%s" % (report_dict(globals()))) 
    print("--- game(): BEF exec: locals:\n%s" % (report_dict(locals()))) 
    global _game_global 
    _game_global = "in main(), BEF execfile()" 
    execfile("script.py") 
    _game_global = "in main(), AFT execfile()" 
    print("--- game(): AFT exec: globals:\n%s" % (report_dict(globals()))) 
    print("--- game(): AFT exec: locals:\n%s" % (report_dict(locals()))) 
    script_mgr.run() 

main()
# script.py 

import script_mgr 
import gamelib # puts gamelib into the local scope of game.py main() 
import pdb # a test import that only shows up in the local scope of game.py main(). It will _not_ show up in any visible scope of ScriptObject.action()! 

class ScriptObject: 
    def action(self): 
    def report_dict(d): 
     s = "" 
     keys = d.keys() 
     keys.sort() 
     for i, k in enumerate(keys): 
     ln = "%04d %s: %s\n" % (i, k, d[k]) 
     s += ln 
     return s 
    print("--- ScriptObject.action(): globals:\n%s" % (report_dict(globals()))) 
    print("--- ScriptObject.action(): locals:\n%s" % (report_dict(locals()))) 
    gamelib.play_sound() 

obj = ScriptObject() 
script_mgr.add_script_object(obj)

這裏是調試輸出

--- game(): BEF exec: globals: 
0000 __builtins__: 
0001 __doc__: None 
0002 __file__: game.py 
0003 __name__: __main__ 
0004 _game_global: BEF main() 
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- game(): BEF exec: locals: 

--- game(): AFT exec: globals: 
0000 __builtins__: 
0001 __doc__: None 
0002 __file__: game.py 
0003 __name__: __main__ 
0004 _game_global: in main(), AFT execfile() 
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- game(): AFT exec: locals: 
0000 ScriptObject: __main__.ScriptObject 
0001 gamelib: 
0002 obj: 
0003 pdb: 
0004 script_mgr: 

--- ScriptObject.action(): globals: 
0000 __builtins__: 
0001 __doc__: None 
0002 __file__: game.py 
0003 __name__: __main__ 
0004 _game_global: in main(), AFT execfile() 
0005 gamelib: 
0006 main: 
0007 report_dict: 
0008 script_mgr: 

--- ScriptObject.action(): locals: 
0000 report_dict: 
0001 self: 


boom!

而不是試圖把在game.py或script.py的模塊級的進口,我會按照由紀子的建議,把import語句在腳本的局部範圍:項目對象成員函數。這對我來說似乎有些尷尬,並且可能有更好的方法來爲執行腳本指定這種導入,但至少現在我明白髮生了什麼。

+0

現在我發現比在成員函數的局部範圍內做進口更好的辦法,我只是做指定ENV:的execfile(「script.py」,script_mgr.env()) 其中script_mgr.env()只是返回script_mgr模塊的globals()字典。這爲script.py導入的範圍提供了一個環境。 – 2010-02-28 18:49:05

相關問題