Как правильно интерпретировать одну строку кода 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
>>>