NameError с использованием исполняемого файла в python

В моем приложении есть кнопка для динамического выполнения скрипта 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?

EDIT: обновлен первый выпуск


person JcMaco    schedule 28.05.2009    source источник
comment
Не могу воспроизвести первую проблему — копирование текста в /tmp/zau и запуск execfile('/tmp/zau') у меня работает нормально. Вторая проблема - это ваша ошибка - bleh - это не глобальная функция, а (неверно определенный) метод класса mainprogram; если вы сделаете bleh статическим методом, сценарий сможет вызывать mainprogram.bleh() [все еще не голое имя 'bleh()', потому что, действительно, это голое имя НЕ определено, имя определено только внутри класса ).   -  person Alex Martelli    schedule 29.05.2009
comment
execfile в первом выпуске также находится внутри класса. Смотрите обновленный вопрос.   -  person JcMaco    schedule 29.05.2009


Ответы (4)


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

Вы стали на 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().

person addy689    schedule 06.02.2013

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

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

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

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

person iksess    schedule 09.12.2013

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

Следует ожидать второй ошибки: имя "bleh" не определено во внешнем блоке, только в пространстве имен "mainprogram"

person Peter Rowell    schedule 28.05.2009