Функция Python exec () не работает в версиях выше 2.7? Ошибка: «имя» не определено - PullRequest
0 голосов
/ 19 марта 2019

Я обнаружил странное поведение функции python exec(). Вот код:

variables = {
    ('foo', 6),
    ('bar', 42)
}

def func():
    for varData in variables:
        varName, varValue = varData
        localVarToEvaluate = varName + ' = varValue'
        try:
            #exec(localVarToEvaluate, globals(), locals())
            exec(localVarToEvaluate)
        except Exception as err:
            print(str(err))

        if varName not in locals():
            print("Variable names '", varName, "can't be found in local scope!")

    if 'foo' in locals():
        print("'foo' OK:", foo)  # exception here
    else:
        print("'foo' not available!")

    if 'bar' in locals():
        print("'bar' OK:", bar)
    else:
        print("'bar' not available!")

func()

Я бы ожидал, что переменные foo и bar будут созданы и напечатаны в конце с помощью вызова exec(), как в случае с Python 2.7. Все вышеперечисленное (проверено на 3.3, 3.4, 3.6 и 3.7) вызывает исключение, что foo не определено:

Exception has occurred: NameError
    name 'foo' is not defined

Странная вещь здесь в том, что foo и bar видны при выполнении locals(), globals() или dir() (также подтверждается if утверждениями), однако, это не видно кодом / переводчик. Даже более странно, отладка этого сценария и разрешение любой переменной успешны (я установил точку останова на # exception here и набрал foo в окне отладки с использованием кода VS. foo правильно разрешен со значением '6'.

Если тот же код (материал внутри функции func()) не заключен в функцию, это работает как положено, foo и bar выводятся на печать.

Есть идеи, что здесь происходит?


ОБНОВЛЕНИЕ: Я еще больше упростил эту проблему:

# take 1, create local variable 'foo' with value 6. Not in function.
varName = 'foo'
varValue = 42
localVarToEvaluate = varName + ' = varValue'

try:
    exec(localVarToEvaluate)
except Exception as err:
    print(str(err))

if 'foo' in locals():
    # print(locals()['foo']) # (1)
    # print(foo)  # (2)
    print("'foo' OK:", foo)  # (3)


# take 2, create local variable 'bar' with value 42
def func2():
    varName = 'bar'
    varValue = 42
    localVarToEvaluate = varName + ' = varValue'

    try:
        exec(localVarToEvaluate)
    except Exception as err:
        print(str(err))

    if 'bar' in locals():
        # print(locals()['bar']) # (1)
        # print(bar)  # (2)
        #print("'bar' OK:", bar)  # (3)
        pass # uncomment any line above

func2()

Когда этот код выполняется, сначала:

'foo' OK: 6

печатается, затем возникает это исключение:

Exception has occurred: NameError
name 'bar' is not defined
  ...

Обратите внимание, что оба кода идентичны, за исключением того, что переменная 'bar' создается внутри функции func2().

Меня интересуют не обходные пути, а объяснение, почему это так и почему пункты (1) работают, а (2) и (3) - нет. Обратите внимание, что переменная bar видна в locals(), хотя она недоступна при непосредственном ее вызове - но только если она создана внутри функции!

Ответы [ 2 ]

0 голосов
/ 26 марта 2019

Я сообщил об ошибке в системе отслеживания проблем Python , и официальный ответ :

Это в настоящее время разработано, что означает 3.8, вероятно, единственное жизнеспособное место, которое он может изменить. Это также не специфично для Windows, поэтому я удалил этот компонент (люди могут удалить себя из любопытного).
...
В настоящее время это в основном прокси-сервер только для чтения, так как локальные функции оптимизированы внутри функций, поэтому вы не можете видеть обновления через канал.

Итог, exec() используемый таким образом, бесполезен внутри функций.

0 голосов
/ 19 марта 2019

Множество неясных вопросов по этому вопросу решено с помощью OP. Смотрите ответы на изменения. Он сводится к (импорту) перехватывающих локальных объектов (переменных, определений, классов), так что они доступны для использования внутри определения.

См. Ответ ниже со встроенными комментариями, что и для чего.

# take 1, create local variable 'foo' with value 6. Not in function.

# >>> code is executed in local-scope <<<

varName = 'foo'
varValue = 42
localVarToEvaluate = varName + ' = varValue'

try:
    exec(localVarToEvaluate)    # (0) dict item becomes {varName : varValue}
    print (localVarToEvaluate)  # (1)  prints > foo = varValue < dict item
except Exception as err:
    print(str(err))

if 'foo' in locals():
    print(locals()['foo'])      # (2)  prints > 42             <             value
    print(foo)                  # (3)  prints > 42             <             value
    print("'foo' OK:", foo)     # (4)  prints > 'foo' OK: 42   < stringtext, value


# take 2, create local variable 'bar' with value 42

def func2(self):

    # >>> code executed inside function and not local-scope <<<

    varName = 'bar'
    varValue = 42
    localVar2Evaluate = varName + ' = varValue'

    try:
        exec(localVar2Evaluate)    # (5) dict item becomes {varName : varValue}
        print (localVar2Evaluate)  # (6) prints > bar = varValue < dict item
    except Exception as err:
        print(str(err))

    print ('local-scope :', '\n', locals())  # (7) {'bar': 42, 'localVar2Evaluate': 'bar = varValue', 'varValue': 42, 'varName': 'bar'}

    if 'bar' in locals():
        print(locals()['bar'])     # (1)
        print(bar)                 # (2)  < --- python is not looking here in the locals() but inside the def for variable `bar` which is not made unless you give it access (hook or reference) via e.g. self.
        print("'bar' OK:", bar)    # (3)
       # pass # uncomment any line above

x = 'a scotch... lets drink.. mystery solved!'
bar = "the local 'bar' variable is now available inside def func2()..  It is: %s" % x
func2(bar)

Как видите, я (импорт) создаю ловушку для локальной переменной с varName 'bar', которая будет использоваться внутри определения с использованием self. Это может быть любое имя t.b.h. См. Pydocs на self и т. Д.

Результат:

bar = varValue
local-scope : 
 {'localVar2Evaluate': 'bar = varValue', 'varValue': 42, 'bar': 42, 'self': "the local 'bar' variable is now available inside def func2()..  It is: a scotch... lets drink.. mystery solved!", 'varName': 'bar'}
42
the local 'bar' variable is now available inside def func2()..  It is: a scotch... lets drink.. mystery solved!
'bar' OK: the local 'bar' variable is now available inside def func2()..  It is: a scotch... lets drink.. mystery solved!

Если print('\n\n', locals()) ниже func (), вы получите следующий результат печати:

  1. 'bar': локальная переменная 'bar' теперь доступна внутри def func2 () .. Это: скотч ... давай выпьем ... загадка разгадана! "
  2. 'localVarToEvaluate': 'foo = varValue'
  3. 'varValue': 42
  4. 'foo': 42
  5. 'varName': 'foo'
  6. 'x': 'скотч ... давай выпьем ... загадка разгадана!'
  7. 'func2': "<" function func2 at 0x000002B070027F28 ">" # без "outside"> ".

В пуле 7 вы видите связанный func2.

ОБНОВЛЕНИЕ 4:

Переключение между python 2.7.16 и 3.5.2 не выявило никаких изменений для dict localals () и ОДНОГО изменения в dict globals (), как показано ниже.

В 2.7: 'variables': set([('bar', 42), ('foo', 6)])

В 3.5: 'variables': {('bar', 42), ('foo', 6)}

... это set() выглядит мне причиной, почему это больше не работает, что вы обратились в 3.5.

Я протестировал его, адаптировав ваш скрипт:

import sys

print (sys.version)

variables = {('foo', 6), ('bar', 42)}

def func():
    for varData in variables:
        varName, varValue = varData
        localVarToEvaluate = varName + ' = varValue'
        try:
            exec(localVarToEvaluate)
            print ('t2\n', locals())
        except Exception as err:
            print(str(err))

        if varName not in globals():
            print("Variable names '", varName, "can't be found in global scope!")

    if 'foo' in globals():
        print("'foo' OK:", foo)  # exception here
    else:
        print("'foo' not available!")

    if 'bar' in globals():
        print("'bar' OK:", bar)
    else:
        print("'bar' not available!")

print ('t1\n', globals())

func()

Тогда ... это все еще может быть exec. Поэтому я отключил запуск func(), а разница в globals() осталась. Так что я думаю, что разница в globals() функция, а не exec.

...