python - 为什么在使用 execfile() 运行的 python 脚本中 import 不能防止 NameError?

标签 python import exec nameerror execfile

当脚本在 Python 中使用 exec 语句或 execfile() 运行时,我查看了一些关于 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)

脚本管理器只是调用每个脚本的 action() 函数:

# 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 $ 

但是,如果我注释掉 game.py 中的“import gamelib”语句并取消注释 script.py 中的“import 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 上

最佳答案

来自Python documentation对于执行文件:

execfile(文件名[, globals[, locals]])

如果省略局部字典,则默认为全局字典。如果两个字典都省略,则在调用 execfile() 的环境中执行表达式。

execfile 有两个可选参数。由于您同时省略了它们,因此您的脚本将在调用 execfile 的环境中执行。因此,在 game.py 中导入会改变行为。

另外,我在game.py和script.py中总结了如下import行为:

  • 在 game.py 中,import gamelib 将 gamelib 模块导入到全局变量和局部变量。这是传递给 script.py 的环境,这就是为什么可以在 ScriptObject 操作方法(从全局变量访问)中访问 gamelib 的原因。

  • 在 script.py 中,import gamelib 将 gamelib 模块导入到locals only(不确定原因)。因此,当尝试从全局变量的 ScriptObject 操作方法访问 gamelib 时,您会遇到 NameError。如果您将导入移动到 action 方法的范围内,它将起作用(gamelib 将从本地访问):

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

关于python - 为什么在使用 execfile() 运行的 python 脚本中 import 不能防止 NameError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2348630/

相关文章:

python - 在一般情况下捕获错误参数异常

python - docker-compose 和 django-haystack

python - 无法安装 jpeg,因为冲突端口处于事件状态 : libjpeg-turbo

Python-sql,使用记录的示例,但收到 'No module named ' sql.aggregate' 错误

java - 通过考虑导入依赖关系来运行java程序

python - 在 Python 中使用 pandas 在现有 Excel 中添加多个工作表

java - 导入 .dat 文件 Java

bash - 如何将多个命令的输出发送到单个 shell 管道?

C Linux - 如何从另一个程序执行程序

linux - 使用 exec 函数时如何使用 gdb 进行调试