在动态加载的脚本中定义的函数不能相互引用

时间:2014-12-04 07:37:13

标签: python ipython read-eval-print-loop execfile

当我在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未定义。”,虽然我在交互式中键入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()

1 个答案:

答案 0 :(得分: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

由于您将两个不同的词组传递给execfile,因此该文件的执行就像它在类定义(from the docs)中一样。这意味着您的功能定义在local_dict但不是global_dict

当你再调用g()时,它将使用全局global_dict和一个新的空本地字典来执行。由于global_dict或新本地人都不包含f,因此我们会收到名称错误。相反,我们正在调用execfile('routines.py', globals())global_dict = globals()local_dict = globals(),因此在f的全局变量中定义了g

编辑:

您注意到local_dict同时包含fg,但第二个示例中没有global_dict。定义任何变量而不明确地将其标记为全局变量将始终生成局部变量,这也适用于模块!通常情况下,模块具有locals() == globals();但是,我们通过使用不同的本地和全球决策来打破这一标准。这就是我说的时候的意思"文件的执行就像是在类定义中一样#34;。