NameError с использованием execfile в Python - PullRequest
4 голосов
/ 29 мая 2009

В моем приложении есть кнопка для динамического выполнения сценария Python с использованием execfile . Если я определяю функцию внутри скрипта (например, spam () ) и пытаюсь использовать эту функцию внутри другой функции (например, eggs () ), я получаю эту ошибку:

NameError: global name 'spam' is not defined

Как правильно вызывать функцию spam () из 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()

Кроме того, я не могу выполнить метод из своего основного приложения в сценарии. т.е.

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

 #my2ndscript.py
 bleh()

Ошибка:

NameError: name 'bleh' is not defined

Как правильно вызвать bleh () из my2ndscript.py ?

РЕДАКТИРОВАТЬ : обновлен первый выпуск

Ответы [ 4 ]

10 голосов
/ 06 февраля 2013

Вы на 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

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

Вывод интерпретатора

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

Из вывода видно, что spam() находится не в глобальном пространстве имен, а в пространстве имен метода runme(). Таким образом, гипотетически, правильный способ вызова spam() был бы

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

Однако нет способа получить доступ к spam(), пока он находится внутри пространства имен runme(). Поэтому решение заключается в том, чтобы вставить spam() в глобальное пространство имен следующим образом:

myscript.py

global spam
def spam():
    print "spam"

def eggs():
    spam()

eggs()

Это обеспечит создание ссылки на объект spam() внутри словаря globals() (т. Е. Глобального пространства имен), что сделает его доступным из eggs().

7 голосов
/ 09 декабря 2013

Addy689 объяснил реальную проблему: именно при вызове execfile () из функции он появляется. execfile () хорошо работает из глобального пространства. Вот почему ответы часто бывают «для меня это работает».

Но решение по изменению вызываемых скриптов может оказаться невозможным. Итак, я сообщаю здесь о решении, которое я считаю лучшим, найденным для другой эквивалентной проблемы с функцией exec () (в этом посте: https://stackoverflow.com/a/11754346/1808778).. То же самое работает с execfile ()

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

Преимущество этого решения в том, что нам не нужно знать вызываемый скрипт: это функция, названная в , если name == main , выполнен.

2 голосов
/ 29 мая 2009

Во втором случае вам понадобится 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()

но это создаст второй экземпляр 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()

Полагаю, что execfile не является правильным решением вашей проблемы в любом случае. Почему бы вам не использовать import или __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()
1 голос
/ 29 мая 2009

Вы уверены, что разместили реальный код, с которым у вас возникли проблемы? Первый скрипт у меня отлично работает.

Ожидается вторая ошибка: имя «bleh» не определено во внешнем блоке, только в пространстве имен «mainprogram»

...