简体   繁体   中英

NameError using execfile in python

My application has a button to execute a python script dynamically using execfile . If I define a function inside the script (eg. spam() ) and try to use that function inside another function (eg. eggs() ), I get this error:

NameError: global name 'spam' is not defined

What is the correct way to call the spam() function from within eggs() ?

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

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

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

def eggs():
    spam()

eggs()

Also, I can't seem to be able to execute a method from my main application in the script. ie

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

 #my2ndscript.py
 bleh()

The error is:

NameError: name 'bleh' is not defined

What is the correct way to call bleh() from my2ndscript.py ?

EDIT : Updated first issue

You're 3 years 8 months wiser since you posted so I'm assuming you'd have figured the first issue out, but given that a solution has not yet been posted (primarily because no one seemed to have a problem with the first issue), the following is my solution.

[UPDATED]

The last solution I provided was incorrect. Below I am providing the correct solution and explaining it in detail using code that I executed.

The problem is inherent in Python's execfile() builtin. Its one of the reasons that function has been deprecated in Python 3.x.

When you executed execfile() inside runme() , the objects spam() and eggs() were loaded into method runme() 's namespace, and not into the global namespace (as they should ideally be) . Consider the following code:

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

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()

Interpreter Output

>>>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>}

From the output you can see that spam() is not in the global namespace, but in method runme() 's namespace. So hypothetically, the correct way to call spam() would have been

def eggs():
    global this
    this.runme.spam()

However, there is no way to access spam() while it is lying inside runme() 's namespace. The solution, therefore, is to insert spam() in the global namespace as follows:

myscript.py

global spam
def spam():
    print "spam"

def eggs():
    spam()

eggs()

This will ensure that a reference to object spam() is created inside the globals() dictionary (ie, the global namespace), making it callable from eggs() .

Addy689 explained the real problem: it is when calling execfile() from a function that it appears. execfile() works well from the global space. That's why answers are often 'for me it works'.

But the solution to modify the called scripts may not be possible. So I report here the solution I think the best one, found on another equivalent problem with the exec() function (in that post: https://stackoverflow.com/a/11754346/1808778 ). It works the same with execfile()

def callingFunction(filename)
    # ... 
    d = dict(locals(), **globals())
    execfile(filename, d, d )

Advantage of that solution is that we don't need to know the called script: it is the function nammed in if name == main that is executed.

In the second case you will need import (not sure whether "mainprogram.py" is on your $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()

but this will create a second instance of mainprogram . Or, better yet:

#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()

I guess that execfile is not the right solution for your problem anyway. Why don't you use import or __import__ (and reload() in case the script changes between those clicks)?

#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()

Are you sure you posted the actual code you are having trouble with? The first script works fine for me.

The second error is to be expected: the name "bleh" is not defined in the outer block, only within the namespace of "mainprogram"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM