1

当我在 ipython 交互式 shell 中时,我试图从脚本中动态加载函数。例如,假设我有一个这样的 python 脚本:

# script.py
import IPython as ip

def Reload():
  execfile('routines.py', {}, globals())

if __name__ == "__main__":
  ip.embed()

假设文件routines.py 是这样的:

# routines.py
def f():
  print 'help me please.'
def g():
  f()

现在,如果我运行脚本 script.py,我将进入交互式 shell。如果我键入以下内容,我对 g() 的调用将起作用:

execfile('routines.py')
g()

但是,如果我键入以下内容,对 g() 的调用将失败:

Reload()
g()

我将收到一条错误消息,提示“未定义全局名称 f”。,尽管当我在交互式 shell 中键入 globals() 时,我仍然可以看到 f 和 g 在输出中。

这两者有什么区别?

更新:

以下工作,但它不是首选解决方案,所以我想为上述问题提供更好的解决方案。

如果我将 script.py 更改为:

# script.py
import IPython as ip

def Reload():
  execfile('routines.py')

if __name__ == "__main__":
  ip.embed()

并将routines.py更改为:

# routines.py

global f
global g

def f():
  print 'help me please.'
def g():
  f()

然后,如果我在交互式 shell 中调用 Reload(),然后调用 g(),它就可以工作。但是,这不是首选方法,因为我必须声明全局名称。

更新 2:

似乎问题与ipython无关。如果我启动 python shell,使用第一个版本的routines.py,然后手动输入以下内容:

def Reload():
  execfile('routines.py', {}, globals())

g()

对 g() 的调用也失败了。但以下工作:

execfile('routines.py')
g()
4

1 回答 1

0

正如@Bakuriu 所说,进口是首选。忽略这一点,你想要的是

def Reload():
    execfile('routines.py', globals())

让我们澄清您的示例以说明它为什么不起作用。

# Setup the namespace to use for execfile
global_dict = {}
local_dict = globals()
execfile('routines.py', global_dict, local_dict)
g() # raises NameError

由于您将两个不同的 dicts 传递给execfile,因此该文件将像在类定义中一样执行(来自 docs)。这意味着您的函数定义在local_dict但不是global_dict.

然后当您调用时g(),它会使用全局变量global_dict和一个新的空本地字典来执行。由于两者global_dict都不包含或新的本地不包含f我们得到一个名称错误。通过调用execfile('routines.py', globals()),我们正在使用global_dict = globals()local_dict = globals()所以fg的全局变量中定义。

编辑:

您注意到它local_dict同时具有fg,但global_dict在第二个示例中没有。定义任何变量而不明确将其标记为全局变量将始终创建一个局部变量,这也适用于模块!碰巧一个模块通常有locals() == globals(); 但是,我们通过使用不同的本地和全局字典打破了这个标准。这就是我说“文件的执行就像它在类定义中一样”时的意思。

于 2014-12-04T11:43:00.430 回答