Использование eval () с неопределенными переменными

1

Я хочу создать скрипт Python, который принимает строковое представление словаря и выводит список кортежей, представляющих элементы словаря. Потребуется, я хочу, чтобы он принимал переменные, которые не были определены. Пример иллюстрирует это лучше всего:

Вход: {'test': test}

Выход: [('test': test)]

Я создал некоторый код для чтения в словаре и определяю переменные, которые еще не определены, но когда я получаю результат eval(), он заменяет фактическое значение переменной, а не имя переменной.

Вот код:

import sys
import re

if __name__ == "__main__":
    instr = sys.argv[1]
    success = False
    while not success:
        try:
            indict = eval(instr)
            success = True
        except NameError, e:
            defname = re.search("name '([^\']*)' is not defined", str(e))
            locals()[defname.group(1)] = 0
    print indict

Я надеялся, что для указанного выше входного значения, что он напечатал, будет идеально соответствовать строке ввода, но вместо этого он заменит значение 0 для test. Таким образом, скрипт печатает:

{'test': 0}

Я пробовал ast.literal_eval, но он выбрасывает ValueError: malformed string для любого имени переменной в литерале, даже если она была определена.

Таким образом, мой вопрос: есть ли способ конвертировать входной словарь без потери имен переменных?

Теги:

5 ответов

0
Лучший ответ

Я решил эту проблему, заменив в уникальных строках имена переменных. Это дает желаемый результат:

import sys
import re

if __name__ == "__main__":
    instr = sys.argv[1]
    subs = []
    success = False
    while not success:
        try:
            indict = eval(instr)
            success = True
        except NameError, e:
            defname = re.search("name '([^\']*)' is not defined", str(e)).group(1)
            subs.append(defname)
            locals()[defname] = '//substitute{%d}' % (len(subs) - 1)
    outlist = indict.items()
    outstr = str(outlist)
    for i, sub in enumerate(subs):
        outstr = outstr.replace("'//substitute{%d}'" % i, sub)
    print outstr

Вход: {"test": test}

Выход: [('test', test)]

Вход: {"test": [{"test": test}, {word: "word"}]}

Вывод: [('test', [{'test': test}, {word: 'word'}])] (обратите внимание, что это желательно, я не хочу, чтобы внутренний словарь застегивался).

Недостатком является то, что я никогда не могу использовать любую строку подстановки, которую я выбираю для использования в любом месте входной строки, но я уверен, что это не будет проблемой для того, для чего я хочу использовать этот скрипт.

2

Я бы использовал модуль ast, но не literal_eval. Пройдите дерево, когда вы найдете ссылку на переменную, просто вставьте имя переменной в вывод.

  • 0
    Согласно определению ast.walk (и моему собственному эксперименту), узлы возвращаются в ast.walk порядке, поэтому я не могу вывести с ним достоверное представление входной строки.
  • 0
    ast.walk не то, что вы хотите. Вам необходимо посетить каждый узел в дереве. См. Для примера stackoverflow.com/questions/1515357/…
1

Вы можете обмануть eval чтобы дать вам то, что вы хотите:

class Reflector(object):
    def __getitem__(self, name):
        return name

s = "{'test':test}"

print eval(s, globals(), Reflector())

производит:

{'test': 'test'}

Обычные предостережения об опасностях eval хотя: если у вас есть какие-либо сомнения относительно источника этих строк, вы открываете себя для взлома.

  • 0
    У меня нет сомнений относительно источника.
  • 0
    Это интересно, но только подводит меня к тому, что вместо переменной есть строка с именем переменной.
0

Я думаю, что разумным обходным путем является переход eval который сохраняет неопределенные переменные полезным способом. Вы можете передать любой подклассы dict для eval, и вы можете переопределить метод __missing__ dict, чтобы вернуть объект неопределенной переменной:

>>> class Var(object):
...     def __init__(self, name):
...         self.name = name
...     def __repr__(self):
...         return "Var(%r)" % (self.name,)
... 
>>> class UndefSubst(dict):
...     def __missing__(self, key):
...         return Var(key)
... 
>>> foo = 'bar'
>>> eval("{'baz': quux, 1: foo}", UndefSubst(locals()))
{1: 'bar', 'baz': Var('quux')}
0

Я думаю, что проблема, с которой вы сталкиваетесь, заключается в том, что вы хотите оценить имя переменной вместо значения этой переменной. В частности, {'test':test} никогда не будет результатом печати словаря, потому что test не является значением в python, возможно, это имя переменной. Самое близкое, что вы можете получить, это присвоение

locals()[defname.group(1)] = defname.group(1)

получить

{'test':'test'}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню