Используйте eval со словарем без потери импортированных модулей в Python2

#python #eval #python-2.x

#python #eval #python-2.x

Вопрос:

У меня есть строка, которая должна выполняться внутри моей программы на python, и я хочу изменить некоторые переменные в строке, такие как x [1], x[2], на что-то другое. Ранее я использовал eval с 2 аргументами (второй — dict с replaced_word: new_word), но теперь я заметил, что не могу использовать ранее импортированные модули, подобные этому. Итак, если я сделаю это

 from math import log
eval(log(x[1], {x[1]: 1})
  

он скажет, что не распознает журнал имен.
Как я могу использовать eval таким образом, не теряя глобальные переменные?
Я не могу разобраться в документации:
https://docs.python.org/2/library/functions.html#eval
так что объяснение тоже было бы полезно.

Ответ №1:

Создайте свои глобальные dict переменные с globals() помощью в качестве основы:

 from math import log

# Copy the globals() dict so changes don't affect real globals
eval_globals = globals().copy()
# Tweak the copy to add desired new global
eval_globals[x[1]] = 1

# eval using the updated copy
eval('log(x[1])', eval_globals)
  

В качестве альтернативы, вы можете использовать три аргумента eval для использования globals() без изменений, но также указать local s dict , которые будут проверены (и изменены) в первую очередь, в предпочтении глобальных значений:

 eval('log(x[1])', globals(), {x[1]: 1})
  

Теоретически, последний подход может позволить выражению изменять исходные глобальные переменные, поэтому добавление .copy() для его создания сводит к eval('log(x[1])', globals().copy(), {x[1]: 1}) минимуму риск случайного возникновения. Но патологический / вредоносный код может обойти это; eval в конце концов, это опасно, не доверяйте ему для произвольных вводов, независимо от того, насколько изолированно вы это делаете.

Комментарии:

1. Копирование глобальных значений не кажется очень эффективным. Кроме того, мне нужно запускать этот eval тысячи раз, поэтому копирование его каждый раз, вероятно, приведет к слишком большим накладным расходам, нет?

2.@JoaquimFerrer: Хех, я просто обновлял ответ, чтобы добавить альтернативные подходы, один из которых устраняет необходимость копирования globals() dict за счет небольшого сокращения песочницы.

3. ввод всегда создается мной, поэтому не беспокойтесь. Это работает отлично, спасибо!

4. @JoaquimFerrer: Всегда пожалуйста. Что касается производительности, да, копирование globals() может стоить дорого. В моей системе globals() это занимает ~ 40 наносекунд, в то время globals().copy() как занимает от 500 наносекунд до 15 секунд использования, в зависимости от того, насколько раздуто глобальное пространство имен. Тем не менее, даже при 15 секундах использования вы можете выполнять это 66 тыс. раз в секунду, поэтому, если вы делаете это тысячи раз, это все равно будет быстрее, чем заметит человек.