Как правильно интерпретировать одну строку кода Python?

Мне нужно выполнить строку кода Python, который вводится пользователем. Если это утверждение, я хочу выполнить его, но если это выражение, я хочу, чтобы результат был возвращен и сделал с ним какие-то интересные вещи. Проблема в том, что в Python для этого есть две разные функции, а именно exec а также eval,

В настоящее время я просто пытаюсь оценить строку, введенную пользователем. Если это вызывает SyntaxError, это может указывать на то, что строка является оператором, поэтому я пытаюсь его выполнить.

try:
    result = eval(command, scope)
except SyntaxError:
    # Probably command is a statement, not an expression
    try:
        exec(command, scope)
    except Exception as e:
        return command + ' : ' + str(e)
except Exception as e:
    return command + ' : ' + str(e)
else:
    pass # Some fancy stuff

Это кажется довольно хакерским. Есть ли более аккуратный, более питонский способ сделать это?

3 ответа

Хотя я думаю, что ваш существующий код, вероятно, достаточно Pythonic (согласно доктрине, что "просить прощения легче, чем разрешение"), я подозреваю, что лучший альтернативный подход - это использовать ast Модуль для проверки кода в вашей строке:

tree = ast.parse(some_input_string)
if len(tree.body) == 1 and isinstance(tree.body[0], ast.Expr):
    result = eval(some_input_string, scope)
else:
    exec(some_input_string, scope)
    result = None

Обратите внимание, что некоторые общие операторы действительно являются выражениями. Итак, входная строка как 'do_stuff("args")' будет использовать eval ветвь кода выше, а не exec ветка. Я не думаю, что это будет иметь какие-либо негативные последствия, но вы никогда не знаете.

Также возможно скомпилировать tree который был проанализирован, а затем передать результат в eval или же exec звонит позже. Я нашел это довольно неудобно, чтобы получить право, хотя (вам нужно обернуть ast.Expr "s value атрибут в ast.Expression в верхней ветке) и поэтому я выбрал более простую (для чтения и понимания) альтернативу - просто передать строку и позволить Python разобрать ее снова.

Вы можете изменить рефакторинг, за исключением немного. В вашем примере нет реального контекста, но при условии, что вы хотите выполнить a=1 а затем оценить a потом и получить 1тогда вы могли бы сделать что-то вроде...

from code import InteractiveConsole
interpreter = InteractiveConsole()

def run(code):

    try: return eval(code.strip(), interpreter.locals)
    except: pass

    try: interpreter.runcode(code)
    except Exception as error: return error

Это также должно работать для более чем одной строки кода.

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

Вы пропустили одну, на самом деле есть три функции, связанные с выполнением кода, и одна из них вы пропустили compile(),

compile() принимает три обязательных аргумента: код для компиляции, имя компилируемого модуля, которое будет отображаться в трассировках, происходящих из этого кода, и "режим". Аргумент mode должен быть одним из "exec" для компиляции целых модулей, "eval" для компиляции простых выражений и "single", который должен быть одной строкой интерактивного ввода!

Во всех трех случаях вы передаете возвращенный code Возражать eval с желаемым контекстом:

>>> c = compile("if 1 < 2:\n    print(3)", "<string>", "single")
>>> eval(c)
3
>>> 
Другие вопросы по тегам