在python中使用execfile的NameError

JcM*_*aco 4 python scope namespaces class

我的应用程序有一个按钮,使用execfile动态执行python脚本.如果我在脚本中定义一个函数(例如spam())并尝试在另一个函数中使用该函数(例如eggs()),我会收到此错误:

NameError: global name 'spam' is not defined
Run Code Online (Sandbox Code Playgroud)

eggs()中调用spam()函数的正确方法是什么?

#mainprogram.py
class mainprogram():
    def runme(self):
        execfile("myscript.py")

>>> this = mainprogram()
>>> this.runme()

# myscript.py
def spam():
    print "spam"

def eggs():
    spam()

eggs()
Run Code Online (Sandbox Code Playgroud)

此外,我似乎无法从脚本中的主应用程序执行方法.即

#mainprogram.py
class mainprogram():
    def on_cmdRunScript_mouseClick( self, event ):
        execfile("my2ndscript.py")
    def bleh():
        print "bleh"

 #my2ndscript.py
 bleh()
Run Code Online (Sandbox Code Playgroud)

错误是:

NameError: name 'bleh' is not defined
Run Code Online (Sandbox Code Playgroud)

my2ndscript.py调用bleh()的正确方法是什么?

编辑:更新了第一期

add*_*689 10

自从你发布以来你已经3年8个月了,所以我假设你已经想出了第一个问题,但考虑到尚未发布解决方案(主要是因为第一个问题似乎没有人出现问题) ),以下是我的解决方案.

[更新]

我提供的最后一个解决方案不正确.下面我提供正确的解决方案,并使用我执行的代码详细解释它.

这个问题是Python execfile()内置的固有问题.它是Python 3.x中不推荐使用该函数的原因之一.

当你执行execfile()里面runme(),对象spam()eggs()装入方法runme()的命名空间,而不是到全局命名空间(因为他们应该理想地).请考虑以下代码:

myscript.py

def spam():
    print 'spam'

def eggs():
    if 'spam' not in globals():
        print 'method spam() is not present in global namespace'
    spam()

try:
    eggs()
except Exception as e:
    print e
Run Code Online (Sandbox Code Playgroud)

mainprogram.py

class mainprogram():
    def runme(self):
        execfile("myscript.py")
        print 'Objects lying in local namespace of runme() are -'
        print locals()

this = mainprogram()
this.runme()
Run Code Online (Sandbox Code Playgroud)

口译员输出

>>>import mainprogram
method spam() is not present in global namespace
name 'spam' is not defined
Objects lying in local namespace of runme() are -
{'e': NameError("name 'spam' is not defined",), 'spam': <function spam at 0x000000000000002B>, 'eggs': <function eggs at 0x000000000000002C>, 'self': <mainprogram.mainprogram instance at 0x000000000000002D>}
Run Code Online (Sandbox Code Playgroud)

从输出中可以看出,spam()它不在全局命名空间中,而是在方法runme()的命名空间中.所以假设,正确的呼叫方式spam()就是这样

def eggs():
    global this
    this.runme.spam()
Run Code Online (Sandbox Code Playgroud)

但是,spam()当它位于runme()命名空间内时,无法访问.因此,解决方案是spam()在全局命名空间中插入如下:

myscript.py

global spam
def spam():
    print "spam"

def eggs():
    spam()

eggs()
Run Code Online (Sandbox Code Playgroud)

这将确保spam()globals()字典(即全局命名空间)内创建对象的引用,使其可以调用eggs().


iks*_*ess 7

Addy689解释了真正的问题:它是它出现的函数调用execfile()时.execfile()在全局空间中运行良好.这就是为什么答案往往"对我来说有效".

但是修改被调用脚本的解决方案可能无法实现.所以我在这里报告我认为最好的解决方案,在exec()函数的另一个等效问题上找到(在该帖子中:https://stackoverflow.com/a/11754346/1808778).它与execfile()的工作方式相同

def callingFunction(filename)
    # ... 
    d = dict(locals(), **globals())
    execfile(filename, d, d )
Run Code Online (Sandbox Code Playgroud)

该解决方案的优点是我们不需要知道被调用的脚本:如果执行了name == main,则该函数被命名为nammed .


lis*_*ine 2

在第二种情况下,您将需要import(不确定“mainprogram.py”是否在您的上$PYTHONPATH

#mainprogram.py
class mainprogram:
    def runme(self):
        execfile("my2ndscript.py")
    def bleh(self):
        print "bleh"
if __name__ == '__main__':
    mainprogram().runme()

#my2ndscript.py
import mainprogram
x = mainprogram.mainprogram()
x.bleh()
Run Code Online (Sandbox Code Playgroud)

但这将创建 的第二个实例mainprogram。或者,更好的是:

#mainprogram.py
class mainprogram:
    def runme(self):
        execfile("my2ndscript.py", globals={'this': self})
    def bleh(self):
        print "bleh"
if __name__ == '__main__':
    mainprogram().runme()

#my2ndscript.py
this.bleh()
Run Code Online (Sandbox Code Playgroud)

我想这execfile无论如何都不是解决您问题的正确方法。为什么不使用importor __import__reload()如果脚本在这些点击之间发生变化)?

#mainprogram.py
import my2ndscript

class mainprogram:
    def runme(self):
        reload(my2ndscript)
        my2ndscript.main(self)
    def bleh(self):
        print "bleh"

if __name__ == '__main__':
    mainprogram().runme()

#my2ndscript.py
def main(program):
    program.bleh()
Run Code Online (Sandbox Code Playgroud)