Порядок присваивания имеет значение неожиданно с «exec expr в globals (), locals ()»

Следующий код в Python 2.X печатает «a: 2», как вы ожидали:

def f(): #a = 1 exec "a = 2" in globals(), locals() for k,v in locals().items(): print k,":",v #a = 3 f() 

Но если вы раскомментируете «a = 1», тогда он печатает «a: 1», как я и не ожидал. Даже более странно, если вы раскомментируете строку «a = 3», то она ничего не печатает, чего я определенно не ожидал (у меня была непонятная ошибка, на которую я догадался).

Я думаю, что ответ похоронен в документации по locals () и globals (), или, может быть, в других вопросах, подобных этому, но я думал, что стоит назвать это проявление.

Мне бы хотелось узнать, что здесь интерпретирует интерпретатор Python, а также предложения об обходных решениях.

Старый исполняемый exec Python 2 изменит байт-код для поиска как локального, так и глобального пространств имен.

Когда вы определяете a = 2 в глобальном, это тот, который найден, когда a = 1 комментируется. Когда вы раскомментируете a = 3 , это «найденный», но еще не определенный.

Если вы читаете, как таблицы символов обрабатываются в этой замечательной статье Эли Бендерски , вы можете лучше понять, как обрабатываются локальные переменные.

Вы не должны использовать exec для этого типа кода (я надеюсь, что это не производственный код), и он все равно сломается при переносе вашего кода на Py3k:

Функция exec Python 3 уже не является оператором и, следовательно, не может изменить окружающую среду, в которой она находится.


Вероятно, я должен перейти прямо к делу:

Если вы делаете все это динамическое именование, вы должны использовать словарь:

 def f(): data = {'a': 1} data['a'] = 2 if ...: data['a'] = 3 

из документации по функциям locals :

Примечание . Содержимое этого словаря не должно изменяться; изменения могут не влиять на значения локальных и свободных переменных, используемых интерпретатором.

проверка:

 >>> def f(): a = 1 print(locals()) locals()['a']=2 print(a,locals()) >>> f() {'a': 1} 1 {'a': 1} 

Таким образом, вы просто не можете изменить локальный контекст через функцию locals()