Поведение Python exec() различается в зависимости от того, откуда он вызывается.

У меня есть скрипт Python runme.py, который я пытаюсь выполнить из callerX.py ниже. Я использую exec(open(filename).read()) для выполнения этой задачи. Выполняемый скрипт содержит простой класс, который пытается вызвать функцию time() как из глобального пространства имен, так и внутри функции.

Во всех приведенных ниже примерах мы выполняем следующий файл с помощью exec():


# runme.py
# this code is being exec()'d by the stand-alone examples 1-3 below:
from time import *

class MyClass():
    def main(self):
        print("Local tracepoint 1")
        t = time()
        print("Local tracepoint 2")

mc = MyClass()
print("Tracepoint 1")
gt = time()
print("Tracepoint 2")
mc.main()
print("Tracepoint 3")


caller1.py: (это работает правильно, функция «время» может использоваться в MyClass.main())


print("Run from main scope:")
exec(open("runme.py").read())

caller2.py: (это не работает, происходит сбой с исключением «NameError: global name 'время' не определено" внутри MyClass.main())

def Run():
    exec(open("runme.py").read())

print("Run from function:") Run()


caller3.py: (это работает правильно, оба exec() выполняются без исключений)

def Run():
    exec(open("runme.py").read())

print("Run from main scope:") exec(open("runme.py").read())

print("Run from function:") Run()

Обратите внимание, что в приведенных выше примерах вызовы функции time() в глобальном пространстве имен runme.py всегда работают, а вызовы функции time() из MyClass.main() работают только иногда, в зависимости от того, или нет, файл runme.py был выполнен exec() из функции.

Если мы вызываем exec() извне функции (caller1.py), она работает. Если мы вызовем exec() из функции (caller2.py), она завершится ошибкой с исключением. Если мы вызываем exec() извне функции, а затем изнутри функции (caller3.py), оба вызовы exec() выполняются без исключения.

Такое поведение кажется непоследовательным. Любые идеи? Я знаю, что это надуманный пример, однако он был взят из гораздо более сложной программы, требования к которой привели нас к этой ситуации.


person octagonC    schedule 26.08.2010    source источник


Ответы (2)


Вероятно, это связано с тем, как работает «импорт из x *». Если вы вызываете это из «верхнего уровня», он импортируется в globals() для всего модуля.

Однако, если вы вызовете это внутри функции, оно будет импортировано только в locals() - в функции. exec() оценивается внутри функции в caller2; поэтому импорт * не попадает в globals(), и «внутренняя» основная функция его не видит.

Кстати: интересно, если вы попробуете такой код:

def run():
    from time import *
    def test():
        print time()
    test()
run()

Вы получите исключение: SyntaxError: импорт * не разрешен в функции «выполнить», поскольку он содержит вложенную функцию со свободными переменными

А вот именно это и делаешь с ехеком, а он как-то неожиданно пролезает.

Однако, учитывая другой ответ здесь, почему бы вам не использовать что-то другое? Посмотрите документацию модуля 'imp' - в частности, функции find_module и load_module.

person ondra    schedule 26.08.2010

Вот идея: не используйте exec. В основном каждый раз, когда я видел, как кто-то использует exec или eval, это потому, что они не знают, что лучший способ выполнить то же самое уже существует; это костыль, мешающий писать динамический код, а не способ написать более динамичный код.

person habnabit    schedule 26.08.2010
comment
Меня беспокоит тот факт, что в этом ответе говорится о лучшем способе сделать это, не давая даже намека на решение. - person JHolta; 25.05.2013